High Performance iOS Apps - Autorelease Pool Blocks 筆記。改寫為 Swift 版本。附加一個(gè) Instrument 測(cè)試。
嵌套的 autoreleasepool
所有 autorelease 中的對(duì)象都會(huì)收到一個(gè) autorelease 的消息,在這個(gè) autorelease block 結(jié)束之后,它們都會(huì)收到 release 通知。
autoreleasepool { () -> () in
//some code
autoreleasepool(invoking: { () -> () in
//some more code
})
}
在同一個(gè)方法中嵌套 autoreleasepool 不是一個(gè)常見的用法。當(dāng)一個(gè)方法傳遞到另一個(gè)方法,被呼叫的方法可以有自己的 autoreleasepool 以便提早釋放對(duì)象。
什么情況下使用?
- 在循環(huán)中創(chuàng)建很多臨時(shí)對(duì)象的時(shí)候
- 自己創(chuàng)建線程的時(shí)候
在循環(huán)中釋放
在循環(huán)內(nèi)部創(chuàng)建 autoreleasepool,這樣可以在每次循環(huán)結(jié)束后釋放掉內(nèi)存,而不是等到所有循環(huán)都結(jié)束了才釋放,大幅降低最大內(nèi)存使用量。
for i in 0..<userCount{
autoreleasepool(invoking: { () -> () in
let p = userDatabase.userAtIndex(i: i) as Person
let fname = p.fname == nil ? p.fname : askUserForFirstName()
let lname = p.lname == nil ? p.lname : askUserForLastName()
p.fname = fname
p.lname = lname
userDatabase.updateUser(p: p)
})
}
在自定義的線程中釋放
每個(gè)線程都有自己的 autoreleasepool block 棧。對(duì)于自定義的線程,你必須創(chuàng)建自己的 autoreleasepool。
func myThreadStart(sender: Any){
autoreleasepool {
}
}
在別的地方
let myThread = Thread(target: self, selector: #selector(myThreadStart), object: nil)
myThread.start()
測(cè)試
運(yùn)行一段消耗內(nèi)存的程序
for _ in 0 ..< 5 {
autoreleasepool {
for _ in 0 ..< 1000 {
let imagex = UIImage(named: "image")
print("\(String(describing: imagex?.description))")
}
}
}
菜單 Product > Profile 調(diào)出 Instrument,選擇 Allocations。點(diǎn)錄制執(zhí)行,然后停止。option + 中鍵放大時(shí)間軸??梢钥吹?5 個(gè)內(nèi)存占用的波形,峰值是 3.29M。每次外部循環(huán)釋放一次內(nèi)存。
修改程序?yàn)檠h(huán)結(jié)束后釋放:
autoreleasepool {
for _ in 0 ..< 5 {
for _ in 0 ..< 1000 {
let imagex = UIImage(named: "image")
print("\(String(describing: imagex?.description))")
}
}
}
內(nèi)存占用一直攀升,釋放前到達(dá)峰值 10.90M。
Statistics 列表中找到占用內(nèi)存較多的一行,點(diǎn)擊右側(cè)箭頭進(jìn)入 Heap & Anonymous VM 列表,選擇產(chǎn)生內(nèi)存占用的條目,右側(cè) Stack Trace 中可以看到棧的最頂端是哪個(gè)類的哪個(gè)方法產(chǎn)生了這部分內(nèi)存。
Stack Trace 中雙擊某一行會(huì)進(jìn)入具體代碼中的位置。