我有一个如下声明的演员协议:

protocol MyActorProtocol: Actor {
    func foo()
}

有一个符合协议的参与者:

actor MyImplementation1: MyActorProtocol {
    func foo() {}
}

现在我需要添加一个代理:

actor MyImplementation1Proxy: MyActorProtocol {
    let impl: MyActorProtocol
    init(impl: MyActorProtocol) {
        self.impl = impl
    }

    func foo() {
        // Error-1: Call to actor-isolated instance method 'foo()' in a synchronous actor-isolated context
        // impl.foo()


        // Error-2. 'await' in a function that does not support concurrency
        // await impl.foo() 


        // Success-3. only this passes the compiler check
        Task { await impl.foo() }
    }
}

我想了解这些点:

  1. 为什么参与者协议可以在没有显式nonisolated关键字的情况下声明非Inbox方法?
  2. 假设有可能有非Inbox方法,那么为什么我的代码中会有Error-1个呢?
  3. 鉴于MyImplementation1Proxy也符合MyActorProtocol,并且MyImplementation1.foo必须在Task中被调用(无论出于何种原因),那么感觉MyImplementation1.foo是"一种Jacobc",所以MyImplementation1Proxy.foo也应该有这种"一种Jacobc上下文",那么为什么我有Error-2呢?
  4. Error-2看起来该方法只是"非执行者",但当我试图引入非执行者实现时,得到了Call to actor-isolated instance method 'foo()' in a synchronous nonisolated context,这很公平,但再次导致问题1:
class MyImplementation2 {
   let impl: MyActorProtocol
   init(impl: MyActorProtocol) {
       self.impl = impl
   }

   func bar() {
       impl.foo()
   }
}

提前感谢.

推荐答案

您正在将隔离与非隔离以及同步与同步混为一谈,这是相互垂直的区别.

与某个参与者实例隔离的方法可以从与该参与者实例隔离的上下文同步调用.否则,必须同步调用它,即用await调用.从不与该参与者隔离的上下文中调用一个隔离方法还涉及"参与者 skip ",它有一些关于Sendable的要求,但我离题了.

async方法必须以everywhere方式被同步调用.您只能在async方法的正文中使用await.

您可以在一个参与者中声明这些组合的所有四种组合,并且协议没有理由不需要这些组合中的任何一种.

actor Foo {
    func f1() { // non-async isolated
        
    }
    
    nonisolated func f2() { // non-async non-isolated
        
    }
    
    func f3() async { // async isolated
        
    }
    
    nonisolated func f4() async { // nonisolated async
        
    }
}

您不能仅仅因为foo不是uc(尽管它是孤立的)而执行await impl.foo().

impl.foo()也无效,因为impl.fooimpl隔离,但您正在从与self(代理)隔离的上下文执行此调用.这些是不同的演员.请注意,这不仅是因为它们的类型不同,还因为selfimpl是不同的instances.这与你不能做这样的事情的原因是相同的:

actor Foo {
    func foo() {
    }
    func bar() {
        // Foo().foo() is isolated to the new instance of Foo, not self!
        Foo().foo()
    }
}

为了使这个代理工作,您必须说服Swift,一旦执行被隔离到self(MyImplementation1Proxy),它也被隔离到impl.

您应该实现unownedExecutor以返回impl的值.然后,用assumeIsolated结束通话.

actor MyImplementation1Proxy: MyActorProtocol {
    let impl: MyActorProtocol
    
    nonisolated var unownedExecutor: UnownedSerialExecutor {
        impl.unownedExecutor
    }
    
    init(impl: MyActorProtocol) {
        self.impl = impl
    }

    func foo() {
        impl.assumeIsolated {
            $0.foo()
        }
    }
}

当某人调用MyImplementation1Proxy.foo时,该调用由MyImplementation1Proxy.unownedExecutor执行,而MyImplementation1Proxy.unownedExecutor恰好与其impl正在使用的任何执行者相同.assumeIsolated判断当前执行人确实是impl.unownedExecutor,并运行foo.

Swift相关问答推荐

ScrollView中的视频渲染不流畅

在用户将输入保存到SWIFT中的核心数据后,如何创建指向我的应用程序S主页的链接?

OBJC代码中Swift 演员的伊瓦尔:原子还是非原子?

SwiftUI map 旋转

按下一步后无法让文本字段切换焦点

以编程方式使UIView居中,而不影响UIKit中其他UI类的位置

swift:只要框架名称中有一个带有框架名称的公共实体,就指定框架中公共 struct 的路径

如何通过 Enum 属性使用新的 #Predicate 宏获取

在 RealmSwift 中表示范围值?

TabView 无法在屏幕上正确显示

覆盖一个子元素的 HStack 对齐方式

NavigationStack 和 TabView - 工具栏和标题不显示

从 iPhone 中的安全飞地获取真正的随机数?

swift 是否遇到 Java 的 2gb 最大序列化大小问题?

如何自己实现同一个 iOS 16 锁屏圆形小部件?

找不到目标AAA的 SPM 工件 - 仅限 Xcode 13.3

Swift 3.0 的 stringByReplacingOccurencesOfString()

在 Swift 框架中加载资源(例如故事板)

Swift 的 JSONDecoder 在 JSON 字符串中有多种日期格式?

swift - 我可以从我的自定义初始化方法中调用 struct 默认成员初始化吗?