最開始只是想試一試寫在方法內(nèi)部的局部變量釋放時經(jīng)不經(jīng)過autoreleasepool罪治。
例如丽声,下圖這樣的代碼。
為了不影響對象本身的引用計(jì)數(shù)影響它的銷毀過程觉义,使用一個weak指針雁社,不出所料的,打印出來了如下結(jié)果
但是這個實(shí)驗(yàn)如果換成NSString得到的則是完全不一樣的結(jié)果晒骇。
如下代碼
打印出來的結(jié)果卻是:
這個看上去也很好似乎也很好理解霉撵,NSString初始化的時候是存放在常量區(qū)的,所以沒有釋放嘛洪囤。
深入研究
為了觀察對象的釋放過程徒坡,我們在str賦值的地方加一個斷點(diǎn)
走到該斷點(diǎn)的時候通過lldb命令watchpoint set variable str來觀察,可以看到str由0x0000000000000000變成0x00000001056b3250瘤缩。
然后一路點(diǎn)擊Continue program execution喇完,發(fā)現(xiàn)str會變成0x0000000000000000,控制臺只打印了一次str的值,也就是說viewwillappear還沒有執(zhí)行剥啤,這點(diǎn)跟雷大博客(Objective-C Autorelease Pool 的實(shí)現(xiàn)原理)中的略不一樣锦溪,我猜是apple改了。
然后看左側(cè)的方法調(diào)用棧府怯,會發(fā)現(xiàn)這個過程經(jīng)過了objc_store,AutoreleasePoolPage::pop(void *)等函數(shù)通過autoreleasepool釋放了】陶铮現(xiàn)在修改一下log語句。
看了幾個大神之前的博客牺丙,大都還打印了retainCount,但是今天這里研究的是arc坏逢,就不打印retainCount了。
執(zhí)行如下代碼:
得到打印結(jié)果:
可以看到這里其實(shí)是有三種String的,而references指向了cstr,此時在viewWillAppear和viewDidAppear里打印references得到的則是null是整。
三種String
- NSCFConstantString: 字符串常量肖揣,放在常量區(qū),對其retain或者release不影響它的引用計(jì)數(shù)浮入,程序結(jié)束后釋放龙优。用字面量語法創(chuàng)建出來的string就是這種,所以在出了viewDidLoad方法以后在其他地方也能打印出值事秀,壓根就沒釋放彤断。
- NSTaggedPointerString: Tagged Point,標(biāo)簽指針易迹,蘋果在64位環(huán)境下對NSString和NSNumber做的一些優(yōu)化宰衙,簡單來說就是把對象的內(nèi)容存放在了指針里,這樣就不需要在堆內(nèi)存里在開辟一塊空間存放對象了睹欲,一般用來優(yōu)化長度較小的內(nèi)容供炼。關(guān)于標(biāo)簽指針的內(nèi)容可以參考唐巧的介紹:深入理解Tagged Pointer
對于NSString,當(dāng)非字面量的數(shù)字窘疮,英文字母字符串的長度小于等于9的時候會自動成為NSTaggedPointerString類型袋哼。代碼中的bstr如果再加一位或者有中文在里面就是變成NSCFString。而NSTaggedPointerString也是不會釋放的闸衫,它的內(nèi)容就在本身的指針里涛贯,又沒有對象釋放個啥啊。所以如果把references的賦值代碼改為
在viewWillAppear和viewDidAppear中也是能打印出值來的蔚出。
NSCFString: 這種string就和普通的對象很像了弟翘,儲存在堆上,有正常的引用計(jì)數(shù)骄酗,需要程序員分配釋放衅胀。所以references = cstr時,會打印出null酥筝,cstr出了方法作用域在runloop結(jié)束時就被autoreleasepool釋放了滚躯。
stringWithFormat
到這里還是有問題
根據(jù)以上說法,當(dāng)string超過10位數(shù)時嘿歌,bstr和cstr都是NSCFString掸掏,可是兩種情況viewWillAppear和viewDidAppear在打印的結(jié)果不一樣。
bstr:
cstr:
根據(jù)太陽神黑幕背后的Autorelease中的說法宙帝,是因?yàn)関iewWillAppear和viewDidLoad在一個runloop中導(dǎo)致bstr在willappear中能打印出來值丧凤。如果真的是這樣那cstr講道理應(yīng)該也能打印出值來,文章最開始用NSObject做實(shí)驗(yàn)時在viewWillAppear時應(yīng)該也能打印出值來步脓。
所以其實(shí)并不是同一個runloop的問題愿待,問題出在stringWithFormat這個工廠方法上浩螺。
查資料得知以 alloc, copy, init,mutableCopy和new這些方法打頭的方法,返回的都是 retained return value仍侥,例如[[NSString alloc] initWithFormat:]要出,而其他的則是unretained return value,例如 [NSString stringWithFormat:]农渊。對于前者調(diào)用者是要負(fù)責(zé)釋放的患蹂,對于后者就不需要了。而且對于后者ARC會把對象的生命周期延長砸紊,確保調(diào)用者能拿到并且使用這個返回值传于,但是并不一定會使用 autorelease,在worst case 的情況下才可能會使用醉顽,因此調(diào)用者不能假設(shè)返回值真的就在 autorelease pool中沼溜。從性能的角度,這種做法也是可以理解的游添。如果我們能夠知道一個對象的生命周期最長應(yīng)該有多長系草,也就沒有必要使用 autorelease 了,直接使用 release 就可以否淤。如果很多對象都使用 autorelease 的話,也會導(dǎo)致整個 pool 在 drain 的時候性能下降棠隐。
也就是說通過工廠方法得到的string生命周期被延長了石抡,所以才會在viewWillAppear里依然可以打印出來。為了證實(shí)這一點(diǎn)助泽,我們換成array來做個實(shí)驗(yàn)啰扛。
第一種情況通過字面量創(chuàng)建array,打印臺輸出:
第二種情況通過工廠方法創(chuàng)建嗡贺,打印臺輸出:
可以看到通過工廠方法創(chuàng)建的array生命周期確實(shí)被延長了隐解。
總結(jié)
1.方法里的臨時變量是會通過autoreleasepool釋放的
2.NSCFString跟普通對象一樣是可以釋放的
3.NSString和NSArray的工廠方法可以延長對象的生命周期(同理,NSDictionary也是一樣的诫睬,有興趣的可以試一下)
——————————————轉(zhuǎn)載自http://www.cocoachina.com/ios/20170303/18829.html