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

时间:2016-04-04 15:03:39

标签: swift retain-cycle

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]的原因?

3 个答案:

答案 0 :(得分:8)

  

...允许列表中的“空”捕获说明符是否有意义?

是的。 捕获说明符("弱","无主"及其变体)只能与引用类型一起使用,但也有一些情况需要捕获值类型 (这是一个例子: 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

答案 1 :(得分:1)

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

  

closure-signature→parameter-clause function-result opt in­
  closure-signature→identifier-list function-result opt ­in­
  closure-signature→capture-list parameter-clause function-result opt in­
  closure-signature→capture-list identifier-list function-result opt ­in
  closure-signature→捕获列表in­
  capture-list→[capture-list-items]
  capture-list-items→capture-list-item capture-list-item capture-list-items
  capture-list-item→capture-specifier opt 表达式
  capture-specifier→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可以找到一种方法来捕获基于表达式类型最有意义的表达式,而无需程序员的任何参与。当编译器没有足够的信息来确定基于上下文捕获变量的正确方法时,显式捕获说明符留给特殊情况。

答案 2 :(得分:0)

回答您的具体问题:

  

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

因为默认行为是捕获任何必要的变量(强烈捕获引用类型)。默认情况下,如果要使用其值,则无需在捕获列表中显式指定它们。 (如果您正在捕获self.property,则需要使用self进行资格认证。)

  

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

出于同样的原因:默认是强烈捕获。 unowned不适用于捕获列表中的其他项目;这不是语法现在如何运作。