autoreleasepool釋放時機

最開始只是想試一試寫在方法內(nèi)部的局部變量釋放時經(jīng)不經(jīng)過autoreleasepool。

例如,下圖這樣的代碼。

xxx.png

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

001.png

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

如下代碼

002.png

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

003.png

這個看上去也很好似乎也很好理解,NSString初始化的時候是存放在常量區(qū)的,所以沒有釋放嘛。

深入研究

為了觀察對象的釋放過程,我們在str賦值的地方加一個斷點

breakpoint.png

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

04.png

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

05.png

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

06.png

看了幾個大神之前的博客,大都還打印了retainCount,但是今天這里研究的是arc,就不打印retainCount了。

執(zhí)行如下代碼:

074.png

得到打印結(jié)果:

066.png

可以看到這里其實是有三種String的,而references指向了cstr,此時在viewWillAppear和viewDidAppear里打印references得到的則是null。

三種String

  • NSCFConstantString: 字符串常量,放在常量區(qū),對其retain或者release不影響它的引用計數(shù),程序結(jié)束后釋放。用字面量語法創(chuàng)建出來的string就是這種,所以在出了viewDidLoad方法以后在其他地方也能打印出值,壓根就沒釋放。
077.png
  • 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的賦值代碼改為

08.png

在viewWillAppear和viewDidAppear中也是能打印出值來的。

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

stringWithFormat

到這里還是有問題

09.png

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

bstr:

10.png

cstr:

11.png

根據(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來做個實驗。

14.png

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

15.png

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

16.png

可以看到通過工廠方法創(chuàng)建的array生命周期確實被延長了。

總結(jié)

1.方法里的臨時變量是會通過autoreleasepool釋放的

2.NSCFString跟普通對象一樣是可以釋放的

3.NSString和NSArray的工廠方法可以延長對象的生命周期(同理,NSDictionary也是一樣的,有興趣的可以試一下)

——————————————轉(zhuǎn)載自http://www.cocoachina.com/ios/20170303/18829.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1、[NSObject alloc]在創(chuàng)建完對象后,會讓該對象的retainCount+1,后續(xù)的init為初始化...
    naiyi閱讀 1,653評論 0 4
  • 目錄 autorelease的本質(zhì) autorelease對象什么時候釋放? autoreleasepool的工作...
    yanhooIT閱讀 5,396評論 5 35
  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴謹 對...
    cosWriter閱讀 11,658評論 1 32
  • 局部釋放池 創(chuàng)建一個新的自動釋放池的方法:ARC下: 這相當于MRC下: 其中對象s會被加入到自動釋放池,當ARC...
    thinkq閱讀 16,931評論 8 40
  • 內(nèi)存的引用 ??計算機是按地址訪問數(shù)據(jù),如果一塊內(nèi)存被使用,就必須讓外界知道它在哪兒,反過來講,要訪問一個數(shù)據(jù)就必...
    吸血鬼de晚餐閱讀 2,178評論 0 5

友情鏈接更多精彩內(nèi)容