最開始只是想試一試寫在方法內(nèi)部的局部變量釋放時經(jīng)不經(jīng)過autoreleasepool。
例如,下圖這樣的代碼。

為了不影響對象本身的引用計數(shù)影響它的銷毀過程,使用一個weak指針,不出所料的,打印出來了如下結(jié)果

但是這個實驗如果換成NSString得到的則是完全不一樣的結(jié)果。
如下代碼

打印出來的結(jié)果卻是:

這個看上去也很好似乎也很好理解,NSString初始化的時候是存放在常量區(qū)的,所以沒有釋放嘛。
深入研究
為了觀察對象的釋放過程,我們在str賦值的地方加一個斷點

走到該斷點的時候通過lldb命令watchpoint set variable str來觀察,可以看到str由0x0000000000000000變成0x00000001056b3250。

然后一路點擊Continue program execution,發(fā)現(xiàn)str會變成0x0000000000000000,控制臺只打印了一次str的值,也就是說viewwillappear還沒有執(zhí)行,這點跟雷大博客(Objective-C Autorelease Pool 的實現(xiàn)原理)中的略不一樣,我猜是apple改了。

然后看左側(cè)的方法調(diào)用棧,會發(fā)現(xiàn)這個過程經(jīng)過了objc_store,AutoreleasePoolPage::pop(void *)等函數(shù)通過autoreleasepool釋放了?,F(xiàn)在修改一下log語句。

看了幾個大神之前的博客,大都還打印了retainCount,但是今天這里研究的是arc,就不打印retainCount了。
執(zhí)行如下代碼:

得到打印結(jié)果:

可以看到這里其實是有三種String的,而references指向了cstr,此時在viewWillAppear和viewDidAppear里打印references得到的則是null。
三種String
- NSCFConstantString: 字符串常量,放在常量區(qū),對其retain或者release不影響它的引用計數(shù),程序結(jié)束后釋放。用字面量語法創(chuàng)建出來的string就是這種,所以在出了viewDidLoad方法以后在其他地方也能打印出值,壓根就沒釋放。

- NSTaggedPointerString: Tagged Point,標簽指針,蘋果在64位環(huán)境下對NSString和NSNumber做的一些優(yōu)化,簡單來說就是把對象的內(nèi)容存放在了指針里,這樣就不需要在堆內(nèi)存里在開辟一塊空間存放對象了,一般用來優(yōu)化長度較小的內(nèi)容。關(guān)于標簽指針的內(nèi)容可以參考唐巧的介紹:深入理解Tagged Pointer
對于NSString,當非字面量的數(shù)字,英文字母字符串的長度小于等于9的時候會自動成為NSTaggedPointerString類型。代碼中的bstr如果再加一位或者有中文在里面就是變成NSCFString。而NSTaggedPointerString也是不會釋放的,它的內(nèi)容就在本身的指針里,又沒有對象釋放個啥啊。所以如果把references的賦值代碼改為

在viewWillAppear和viewDidAppear中也是能打印出值來的。
NSCFString: 這種string就和普通的對象很像了,儲存在堆上,有正常的引用計數(shù),需要程序員分配釋放。所以references = cstr時,會打印出null,cstr出了方法作用域在runloop結(jié)束時就被autoreleasepool釋放了。
stringWithFormat
到這里還是有問題

根據(jù)以上說法,當string超過10位數(shù)時,bstr和cstr都是NSCFString,可是兩種情況viewWillAppear和viewDidAppear在打印的結(jié)果不一樣。
bstr:

cstr:

根據(jù)太陽神黑幕背后的Autorelease中的說法,是因為viewWillAppear和viewDidLoad在一個runloop中導致bstr在willappear中能打印出來值。如果真的是這樣那cstr講道理應(yīng)該也能打印出值來,文章最開始用NSObject做實驗時在viewWillAppear時應(yīng)該也能打印出值來。
所以其實并不是同一個runloop的問題,問題出在stringWithFormat這個工廠方法上。
查資料得知以 alloc, copy, init,mutableCopy和new這些方法打頭的方法,返回的都是 retained return value,例如[[NSString alloc] initWithFormat:],而其他的則是unretained return value,例如 [NSString stringWithFormat:]。對于前者調(diào)用者是要負責釋放的,對于后者就不需要了。而且對于后者ARC會把對象的生命周期延長,確保調(diào)用者能拿到并且使用這個返回值,但是并不一定會使用 autorelease,在worst case 的情況下才可能會使用,因此調(diào)用者不能假設(shè)返回值真的就在 autorelease pool中。從性能的角度,這種做法也是可以理解的。如果我們能夠知道一個對象的生命周期最長應(yīng)該有多長,也就沒有必要使用 autorelease 了,直接使用 release 就可以。如果很多對象都使用 autorelease 的話,也會導致整個 pool 在 drain 的時候性能下降。
也就是說通過工廠方法得到的string生命周期被延長了,所以才會在viewWillAppear里依然可以打印出來。為了證實這一點,我們換成array來做個實驗。

第一種情況通過字面量創(chuàng)建array,打印臺輸出:

第二種情況通過工廠方法創(chuàng)建,打印臺輸出:

可以看到通過工廠方法創(chuàng)建的array生命周期確實被延長了。
總結(jié)
1.方法里的臨時變量是會通過autoreleasepool釋放的
2.NSCFString跟普通對象一樣是可以釋放的
3.NSString和NSArray的工廠方法可以延長對象的生命周期(同理,NSDictionary也是一樣的,有興趣的可以試一下)
——————————————轉(zhuǎn)載自http://www.cocoachina.com/ios/20170303/18829.html