2015-02-07 81 views
2

我在操场代码(我使用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所以可以deinitalized?

回答

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.ff,它是完美的罚款:

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 

斯威夫特走出这一步,虽然,“咖喱”的第一个参数,这样就可以写c.f并获得结合fC特定实例的功能:

// 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 

这相当于在您的原始问题代码中的捕获,应该显示为什么你没有看到你原来的一个对象被销毁。

顺便说一句,如果你真的想用一个封闭的表达打电话给你的方法(假设你想要做之前或之后,更多的工作),你可以使用一个变量捕获列表:

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 
+0

感谢您的详细解答!我的目的是将另一个类的功能保存为一个变量,而不创建一个强大的参考文献。我真的很喜欢你提到捕获列表的最后一个例子,并且如果我错了,请纠正我:让我在 c?.f() }中解决我的问题。不创建强大的参考周期(如果我正确地处理可选项)。 – 2015-02-08 15:10:31

+0

是的你是对的,这是一个避免强循环的好方法。尽管如此,如果您确定您拥有的对象的生命周期将与封闭所有者的生命周期相匹配,您可能还想考虑“无主”而不是“弱” - 尽管这受到与指针,如果你错了一生。 – 2015-02-08 15:13:48

1

p1.memory.returnFive in TestClassB(closure: p1.memory.returnFive)是一个curried函数func returnFive() -> Int绑定到ClassA的实例。它拥有对实例的引用。

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

因此,在第二种情况下,p1.memory是参考ClassA实例的唯一所有者。这就是为什么p1.destroy()取消分配它。

+0

东西是不清楚:如果我将使用实例而不是MutablePointers,那么在第二种情况下也不会让TestClassA去初始化? – 2015-02-07 18:18:45