将闭包保存为变量的理解

Saving closure as a variable understanding

我正在操场上测试这段代码(我正在使用 UnsafeMutablePointers 来模拟取消初始化):

class TestClassA {

    func returnFive() -> Int {
        return 5
    }

    deinit {
        println("Object TestClassA is destroyed!") //this way deinit is not called
    }
}

class TestClassB {

    let closure: () -> Int

    init(closure: () -> Int) {
        self.closure = closure
    }

    deinit {
        println("Object TestClassB is destroyed!")
    }
}

let p1 = UnsafeMutablePointer<TestClassA>.alloc(1)
p1.initialize(TestClassA())

let p2 = UnsafeMutablePointer<TestClassB>.alloc(1)
p2.initialize(TestClassB(closure: p1.memory.returnFive))

p2.memory.closure()
p1.memory.returnFive()


p1.destroy()

但是,当我将 TestClassB 的初始化更改为:

p2.initialize(TestClassB(closure: {p1.memory.returnFive()}))

现在可以取消初始化 TestClassA。

那么谁能告诉我,两者有什么区别

TestClassB(closure: p1.memory.returnFive)

TestClassB(closure: {p1.memory.returnFive()})

为什么在第二种情况下没有对 TestClassA 的强引用,所以它可以被取消初始化?

TestClassB(closure: p1.memory.returnFive) 中的

p1.memory.returnFive 是绑定到 ClassA 实例的柯里化函数 func returnFive() -> Int。它拥有对实例的引用。

另一方面,{p1.memory.returnFive()} 只是一个捕获 p1 变量的闭包。此闭包没有引用 ClassA 本身的实例。

因此,在第二种情况下,p1.memory 是对 ClassA 实例的引用的唯一所有者。这就是 p1.destroy() 释放它的原因。

这里的问题是UnsafeMutablePointer<SomeStruct>.memory的使用。重要的是不要陷入认为 memory 就像一个包含指向对象的存储 属性 的陷阱,只要指针存在,它就会保持活动状态。尽管感觉像一个,但它不是,它只是原始记忆。

这是一个只使用一个 class 的简化示例:

class C {
    var x: Int
    func f() { println(x) }

    init(_ x: Int) { self.x = x; println("Created") }
    deinit { println("Destroyed") }
}

let p = UnsafeMutablePointer<C>.alloc(1)
p.initialize(C(42))
p.memory.f()
p.destroy()   // “Destroyed” printed here
p.dealloc(1)
// using p.memory at this point is, of course, undefined and crashy...
p.memory.f()

但是,假设您复制了 memory 的值并将其分配给另一个变量。这样做会增加 memory 指向的对象的引用计数(就像你复制另一个常规 class 引用变量一样:

let p = UnsafeMutablePointer<C>.alloc(1)
p.initialize(C(42))
var c = p.memory
p.destroy()   // Nothing will be printed here
p.dealloc(1)
// c has a reference
c.f()
// reassigning c decrements the last reference to the original
// c so the next line prints “Destroyed” (and “Created” for the new one)
c = C(123)

现在,假设您创建了一个捕获 p 的闭包,并在 p.destroy() 被调用后使用了它的内存:

let p = UnsafeMutablePointer<C>.alloc(1)
p.initialize(C(42))
let f = { p.memory.f() }
p.destroy()   // “Destroyed” printed here
p.dealloc(1)
// this amounts to calling p.memory.f() after it's destroyed,
// and so is accessing invalid memory and will crash...
f()

但是,对于您的情况,如果您只是将 p.memory.f 分配给 f,那就完全没问题了:

let p = UnsafeMutablePointer<C>.alloc(1)
p.initialize(C(42))
var f = p.memory.f
p.destroy()  // Nothing will print, because
             // f also has a reference to what p’s reference
             // pointed to, so the object stays alive
p.dealloc(1)
// this is perfectly fine
f()
// This next line will print “Destroyed” - reassigning f means 
// the reference f has to the object is decremented, hits zero, 
// and the object is destroyed 
f = { println("blah") }

那么 f 是如何获取值的?

正如@rintaro 所指出的,Swift 中的成员方法是柯里化函数。想象一下没有成员方法。相反,只有常规函数和具有成员变量的结构。你怎么能写出等价的方法呢?你可以这样做:

// a C.f method equivalent.  Using this
// because self is a Swift keyword...
func C_f(this: C) {
    println(this.x)
}
let c = C(42)
// call c.f()
C_f(c)  // prints 42

Swift 更进一步,“柯里化”了第一个参数,这样你就可以编写 c.f 并获得一个将 f 绑定到特定实例的函数的 C

// C_f is a function that takes a C, and returns
// a function ()->() that captures the this argument:
func C_f(this: C) -> ()->() {
    // here, because this is captured, it’s reference
    // count will be incremented
    return { println(this.x) }
}

let p = UnsafeMutablePointer<C>.alloc(1)
p.initialize(C(42))
var f = C_f(p.memory)  // The equivalent of c.f
p.destroy()   // Nothing will be destroyed
p.dealloc(1)
f = { println("blah") } // Here the C will be destroyed

这等同于您原始问题代码中的捕获,并且应该显示为什么您没有看到原始 A 对象被销毁。

顺便说一句,如果你真的想使用闭包表达式来调用你的方法(假设你想在之前或之后做更多的工作),你可以使用变量捕获列表:

let p = UnsafeMutablePointer<C>.alloc(1)
p.initialize(C(42))
// use variable capture list to capture p.memory
let f = { [c = p.memory] in c.f() }
p.destroy()   // Nothing destroyed
p.dealloc(1)
f()   // f has it’s own reference to the object