将闭包保存为变量的理解
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
我正在操场上测试这段代码(我正在使用 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