High Performance iOS Apps - Autorelease Pool Blocks 筆記前方。改寫為 Swift 版本诲宇。附加一個(gè) Instrument 測試取逾。
嵌套的 autoreleasepool
所有 autorelease 中的對象都會收到一個(gè) autorelease 的消息逆趣,在這個(gè) autorelease block 結(jié)束之后荆几,它們都會收到 release 通知吓妆。
autoreleasepool { () -> () in
//some code
autoreleasepool(invoking: { () -> () in
//some more code
})
}
在同一個(gè)方法中嵌套 autoreleasepool 不是一個(gè)常見的用法。當(dāng)一個(gè)方法傳遞到另一個(gè)方法吨铸,被呼叫的方法可以有自己的 autoreleasepool 以便提早釋放對象行拢。
什么情況下使用?
- 在循環(huán)中創(chuàng)建很多臨時(shí)對象的時(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 棧沼瘫。對于自定義的線程,你必須創(chuàng)建自己的 autoreleasepool咙俩。
func myThreadStart(sender: Any){
autoreleasepool {
}
}
在別的地方
let myThread = Thread(target: self, selector: #selector(myThreadStart), object: nil)
myThread.start()
測試
運(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 中雙擊某一行會進(jìn)入具體代碼中的位置以躯。