我在游乐场测试此代码(我使用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的强引用所以它可以被去除资本化?
答案 0 :(得分:2)
这里的问题是使用UnsafeMutablePointer<SomeStruct>.memory
。重要的是不要陷入这样的陷阱:memory
就像包含指向对象的存储属性一样,只要指针存在就会保持活着状态。即使感觉像是一个,但它不是,它只是原始记忆。
这是一个只使用一个类的简化示例:
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
的引用计数(就像你获取另一个常规类引用变量的副本一样:
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中的成员方法是curried函数。想象一下,没有成员方法。相反,只有常规函数和具有成员变量的结构。你怎么写相同的方法?你可能会这样做:
// 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更进一步,然后“curry”第一个参数,这样你就可以编写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
答案 1 :(得分:1)
p1.memory.returnFive
中的 TestClassB(closure: p1.memory.returnFive)
是一个与func returnFive() -> Int
实例绑定的curry函数ClassA
。它拥有对实例的引用。
另一方面,{p1.memory.returnFive()}
只是一个捕获 p1
变量的闭包。此闭包没有引用ClassA
本身的实例。
因此,在第二种情况下,p1.memory
是ClassA
实例引用的唯一所有者。这就是p1.destroy()
解除分配的原因。