2016-01-22 69 views
0
class Person { 
var hello : (() -> Void)? 
var name = "name" 

init() { 
    print("init \(self)") 
} 

deinit { 
    print("deinit \(self)") 
} 
} 

var person : Person! 

person = Person() 
person.hello = {() -> Void in 
    print("\(person.name)") 
} 
person = nil 

和控制檯輸出爲:爲什麼這不會導致Swift中的保留週期?

init Person 
deinit Person 

在我看來,這是因爲「人」是可選的,因此斯威夫特保持在「你好」關閉弱引用,是這樣嗎?

+0

可選項沒有微弱存儲。如果這是你想要的,你需要聲明'人'爲'弱'。但是弱對象*必須是可選項。 – MaddTheSane

+0

這不是原來的問題,請不要改變它,直到你得到所有的答案。 – Cristik

回答

1

因爲html是一個可選項,所以你的閉包實際上持有對enum(可選)的引用,而不是對象本身,所以只有一個對Person()的引用。

如果您創建了一個html實際上超出了範圍的情況,那麼對Person()對象的引用會一直存在,因爲捕獲將是對html變量唯一的剩餘引用。 但它仍然不是具有引用計數的Person()對象。

class Person { 
var hello : (() -> Void)? 
var name = "name" 

init() { 
    print("init \(self)") 
} 

deinit { 
    print("deinit \(self)") 
} 
} 

repeat 
{ 
    var html : Person 

    html = Person() 
    // this closure keep a reference to 'html' 
    html.hello = {() -> Void in 
     print("\(html.name)") 
    } 

} while false 
+0

將變量聲明爲可選不會影響它的保留方式。你持有一個對'Optional'枚舉的引用,該枚舉繼而持有對盒裝值的引用,所以你仍然得到一個保留。 – Cristik

+0

這不是我說的。我指出的是,在示例代碼中,html變量在某個時間點的refcount爲2,但Person()永遠不會獲得超過1的refcount(在我的示例中,同樣的事情)。 –

+0

明白了,謝謝,我誤解了你答案中的第一句話。 – Cristik

1

你不會得到保留循環,因爲hello閉合捕捉一個變量,而不是一個常數,是因爲變量是有望在將來改變,它不保留它,它只是持有對它的引用。

將其轉換爲一個常數,你會獲得預期的保留週期:

func test() { 
    let html = Person() 
    html.hello = {() -> Void in 
     print("\(html.name)") 
    } 
} 
test() 

這僅打印init Person,雖然功能結束時,html超出範圍,應被釋放。

如果將變量定義爲__block,則該行爲與Objective-C等效。