【问题标题】:Why is the capture specifier optional in capture lists?为什么捕获列表中的捕获说明符是可选的?
【发布时间】:2016-04-04 15:03:39
【问题描述】:

在 Swift 中的捕获列表语法中似乎有一个奇怪的语法故障。如果我声明多个捕获的变量,则捕获说明符仅适用于第一个:

let closure = { [unowned x, y] in … }

现在我希望 yunowned,但事实并非如此:

class Test {

    var callback: (Void -> Void)?

    init() {
        print("init")
    }

    deinit {
        print("deinit")
    }
}

func makeScope() {
    let x = Test()
    let y = Test()
    y.callback = { [unowned x, y] in
        print(y)
    }
}

makeScope()
print("done")

打印出来:

init
init
deinit
done

所以y 似乎被强烈捕获并创建了一个保留循环,防止对象被释放。是这样吗?如果是,在列表中允许“空”捕获说明符是否有意义?或者[unowned x, y]不被视为[unowned x, unowned y]有什么原因?

【问题讨论】:

    标签: swift retain-cycle


    【解决方案1】:

    ...在列表中允许“空”捕获说明符有意义吗?

    是的。 捕获说明符(“weak”、“unowned”及其变体)只能与引用类型一起使用,但也存在您想要捕获值类型的情况 (这里是一个例子: Pass value to closure?)。

    您可能还想强烈地捕获引用类型。 捕获引用类型可确保引用(指针) 本身是按值捕获的,如下例所示:

    class MyClass {
        let value : String
        init(value : String) {
            self.value = value
        }
    }
    
    var ref = MyClass(value: "A")
    
    let clo1: () -> Void = { print(ref.value) }
    let clo2: () -> Void = { [ref] in print(ref.value) }
    
    ref = MyClass(value: "B")
    
    clo1() // Output: B
    clo2() // Output: A
    

    当第一个闭包执行时,闭包内部的ref 是对创建为 MyClass(value: "B") 的对象的引用。

    第二个闭包捕获ref的值 闭包被创建,并且当一个新值时这不会改变 分配给var ref

    【讨论】:

    • 很好,谢谢。您认为当引用类型前面缺少说明符时,编译器至少产生警告是否有意义?
    • @zoul:不,见上面的例子。
    • 太好了,谢谢!我认为让我不高兴的是unowned x, y 的简单、常见情况看起来具有误导性(参见类 C 语言中的int a, b),而无说明符引用类型捕获的罕见情况很容易(而且更明确! ) 使用临时变量来解决。
    【解决方案2】:

    根据syntax EBNF,这种对unowned的处理捕获说明符是完全有意的:

    闭包签名→参数子句函数结果optin­
    闭包签名 → 标识符列表函数结果opt­in­
    闭包签名 → 捕获列表参数子句函数结果optin­
    闭包签名 → 捕获列表标识符列表函数结果opt­in
    闭包签名 → 捕获列表 in­
    捕获列表 → [捕获列表项]
    捕获列表项 → 捕获列表项 捕获列表项 捕获列表项
    捕获列表项 → 捕获说明符opt 表达式
    捕获说明符 → weak | unowned | unowned(safe) | unowned(unsafe)­

    底部定义<capture-list-items><capture-list-item><capture-specifier> 产生式的三行与此处最相关。

    <capture-list-items> 产生式是 <capture-list-item> 的逗号分隔列表,capture-specifier 附加到每个单独的 <capture-list-item>,而不是整个 <capture-list-items> 列表。

    这很有意义,因为它使程序员可以完全控制单个参数的捕获。说明符应用于整个列表的替代方案将取消这种灵活性。

    为什么要在捕获列表中包含一个标识符而不修改其捕获说明符?

    似乎 Swift 设计者的理念是尽可能提供智能的默认行为。在大多数情况下,Swift 可以找到一种方法来捕获基于表达式类型最有意义的表达式,而无需程序员参与。当编译器没有足够的信息来确定基于上下文捕获变量的正确方法时,显式捕获说明符被保留用于例外情况。

    【讨论】:

    • 好吧,问题是为什么要在捕获列表中包含一个标识符而不修改其捕获说明符?但正如 Martin 所说,这实际上对值类型有意义。尽管如此,引用类型的行为仍然是一个等待发生的错误,恕我直言。
    【解决方案3】:

    回答您的具体问题:

    为什么捕获列表中的捕获说明符是可选的?

    因为默认行为是捕获任何必要的变量(引用类型被强捕获)。默认情况下,如果要使用它们的值,则无需在捕获列表中明确指定它们。 (尽管如果您要捕获self,则必须使用self.property 进行限定。)

    ...[unowned x, y] 不被视为[unowned x, unowned y] 是否有原因?

    同理:默认是强捕获。 unowned 不适用于捕获列表中的其他项目;现在的语法不是这样的。

    【讨论】:

      猜你喜欢
      • 2014-12-21
      • 2013-03-04
      • 2012-09-22
      • 1970-01-01
      • 2013-01-25
      • 1970-01-01
      • 1970-01-01
      • 2013-07-10
      • 1970-01-01
      相关资源
      最近更新 更多