變數與常數的生命週期
(1.) Function
Function的參數, 區塊{ }內的變數與常數會隨著執行任務的結束而釋放記憶體空間
Copy func test ( text : String ) {
var value1: Int = 1
let value2: Int = 2
}
test ( "haha" )
//此時釋放 text, value1, value2 記憶體
(2.) Structure
Structure區塊{ }內的變數與常數會隨著Structure變數不再被參考到而釋放記憶體空間
Copy struct Test {
var value1: Int = 1
let value2: Int = 2
}
var test: Test ? = Test ()
test = nil //此時釋放 value1, value2 記憶體
(3.) Class
同樣的, Class區塊{ }內的變數與常數會隨著Class變數不再被參考到而釋放記憶體空間
Copy Class Test {
var value1: Int = 1
let value2: Int = 2
}
var test: Test ? = Test ()
test = nil //此時釋放 value1, value2 記憶體
類別解構函式deinit
deinit{}
會在Class release前被呼叫, 我們可以利用這個function觀察Class是否有被釋放或執行一些動作
Copy class Test {
var title: String
init ( title : String ) {
self.title = name
println ( "class is init" )
}
deinit {
println ( "Class is released" )
}
}
var test = Test ( name : "Hemingway" ) //output: "class is init"
test = nil //output: "Class is released"
Reference Cycle
Swift是遵循著自動引用計數 (ARC) 的規則, 通常當一個物件沒有被其他對象引用時會自動被銷毀, 但有些情況下物件會互相參考, 行成Reference Cycle, 造成物件的記憶體永遠不會被釋放.
(1.) 經典例子: Closure
Copy Class Test {
typealias Complete = () -> ()
var name: String
var onComplete : Complete ?
init ( _ name : String ) {
self.name = name
onComplete = {
print ( " \(self.name) : onComplete!" ) // --> Closure引用self, reference count + 1
}
}
deinit {
print ( "deinit: \(self.name) " )
}
}
var a: Test ? = Test ( "A" ) // --> reference count + 1
a = nil // reference count: 2 - 1 = 1, 還剩下1故無法釋放
Reference Cycle : self擁有了onComplete, onComplete也擁有self
Solution:
若是在release Test 物件之前, 先release onComplete, 則可以正常釋放物件
Copy var b : Test ? = Test ( "B" ) // reference count = 2
b ? .onComplete = nil // --> reference count - 1
b = nil //reference count - 1, reference count = 0, 被釋放
但有更好的做法, 使用unowned 或weak 關鍵字
Copy onComplete = { [ unowned self] in
print ( " \(self.name) : onComplete!" ) // --> 不會強制持有 self
}
(2.)經典例子: Apple官方例子
Copy class User {
var name : String
var lover: User ?
init ( _ name : String ){
self.name = name
}
func setLover ( _ user : User) {
lover = user
}
deinit {
print ( "deinit: \(self.name) " )
}
}
var A = User ( "A" )
var B = User ( "B" )
A. setLover ( B ) //互相引用, reference cycle
B. setLover ( A )
A = nil // 因為B.lover引用了A, 所以a不會正確被釋放
print ( B ? .lover ! ) //此時A還存在
B = nil // 因為B也等於nil了, 沒有人可以再參考到A.lover與B.lover, 記憶體永遠無法被釋放了
Solution:
在 A = nil
之後 把B?.lover
也設成nil
, 則可以正常釋放記憶體
Copy A = nil
B ? .lover = nil // 無人再參考到A, A被回收了
B = nil // B 也可以順利回收了
但有更好的做法, 使用weak 關鍵字
Copy class User {
var name : String
weak var lover: User ?
init ( _ name : String ){
self.name = name
}
func setLover ( _ user : User) {
lover = user
}
deinit {
print ( "deinit: \(self.name) " )
}
}
var A = User ( "A" )
var B = User ( "B" )
A. setLover ( B ) //互相引用, reference cycle
B. setLover ( A )
A = nil // B.lover引用了A, 但因為是weak, 所以也會被釋放
print ( B ? .lover ! ) //此時A不存在了
B = nil // A, B皆釋放了
unowned 與 weak
(1.) unowned用於非optional的變數
unowned 通常都用於該物件不會比或持有它的物件更早被release,
[unowned self]
可以看做self!
Copy onComplete = { [ unowned self] in
print ( " \(self.name) : onComplete!" ) // --> unowned self
}
(2.) weak用於optional的變數
如遇到網路request, user在 response回來之前就返回上一頁, 使的物件有可能會比或持有它的物件更早被release, 則必須使用weak, [weak self]
可以看做self?
(若使用unowned
此時會造成crash)
Copy onComplete = { [ weak self] in
if let strongSelf = self {
print ( " \(strongSelf.name) : onComplete!" )
}
}