0枷餐、簡單的說一句
autorelease 已經(jīng)在 iOS 界叱咤風(fēng)云這么多年,現(xiàn)在網(wǎng)上也有很多類似的文章,今天也來造個輪子仓手。關(guān)于 autorelease 往往會出現(xiàn)這三個問題:
- 1攻礼、是做什么的业踢,有什么用
- 2、autorelease 后的對象礁扮,是在什么時候被銷毀的
- 3知举、__autoreleasing 的使用
當(dāng)然,很多的時候提問者是直接問第2個問題,只要第2問回答正確了深员,都沒有問題了负蠕。如果是你,你會怎么回答倦畅。關(guān)于這個問題遮糖,我會先給出常規(guī)的錯誤答案,然后逐一解釋叠赐。
建議:本文主要是看過程欲账、不要追求最終的答案。想看最后答案芭概,直接看第三部分赛不。
一、錯誤回答
你是否會這樣的回答罢洲?踢故!當(dāng)運行跳出大括號之后文黎,會給當(dāng)前自動釋放池中發(fā)送過 autorelease 消息的對象都發(fā)送一條 release 消息。貌似很多的小伙伴都會這么回答殿较,遺憾的是 這個回答是錯誤的耸峭,接下來會給出錯誤的理由。十分的題淋纲、也就對了1分劳闹,在職場上回答不到8分的回答都是0分。
為什么是錯誤的回答呢洽瞬?因為與 大括號 沒有 多大 的關(guān)系本涕。具體解釋,請看下文伙窃。
在開始解釋之前菩颖,先定義一個Class,命名為:HGObject为障,定義如下:
#import <Foundation/Foundation.h>
@interface HGObject : NSObject
/** 名稱 */
@property (nonatomic, copy) NSString* name;
@end
====調(diào)皮的分割線====
#import "HGObject.h"
@implementation HGObject
- (void)dealloc {
NSLog(@" %@ 被釋放", _name);
[super dealloc];
}
@end
總的來說位他,就是定義了一個屬性 name,然后重寫了一下 -dealloc 方法产场。從上面的帶來來看鹅髓,我們接下來的演示中是在 MRC 環(huán)境下進行的。
二京景、與大括號的關(guān)系
2.1 不會被釋放的情況
你看了之后窿冯,你會說:這肯定不會被釋放的,因為沒有再釋放池中确徙。 對醒串、很有道理,確實也是這樣鄙皇,autorelease 必須要在池子中才會有效芜赌。那么我就再遷移到池子中,看一下效果伴逸。
結(jié)果還是一樣的:不會被釋放缠沈。看到這里的你是不是很激動,不信你就試試错蝴,如果釋放了記得告訴我洲愤。關(guān)于這種情況,也有這么解釋的:因為這里的 autoreleasepool 沒有被釋放顷锰,所以不會被釋放柬赐,這種解釋 100% 是錯誤的解釋。但是如果上面的兩個地方將 autorelease 換成 release 官紫,那么都是可以釋放的肛宋。
到這里州藕,是不是就說明了與大括號無關(guān)了?酝陈!
來簡單的分析一下慎框,在上面的兩種使用 autorelease 之后不會被釋放的情況。在上面的兩個地方后添,是一個非常不應(yīng)該的特例,因為我們一般不會在這個地方寫代碼薪丁。但是有一點很情況的是:在上面的兩個地方都沒有被進入 UIApplicationMain遇西,在 UIApplicationMain 有一個特別重要的機制,叫:運行循環(huán)严嗜,美其名曰 NSRunLoop粱檀。如果說能想到這一點,那么就算是入門了漫玄。
看到上面的介紹茄蚯,你會不會又會很激動的說:對啊,我說的就是在運行循環(huán)中的與大括號有關(guān)的睦优,在大括號結(jié)束之后就釋放了渗常。 還想說的是,這也是錯誤的汗盘。是與大括號有關(guān)系皱碘,但是并不是 那 一層的關(guān)系。
2.2 與大括號的那一層關(guān)系
先看一下這個代碼:
關(guān)于上面的代碼隐孽,當(dāng)我點擊屏幕的時候癌椿,會打印
兩個 log 日志信息, 是先打印 testAutorelease 執(zhí)行完畢 呢菱阵,還是先打印 obj 對象 被釋放踢俄。思考一下、把你的答案放在心中晴及,稍后公布答案都办。
、
虑稼。
脆丁,
、
动雹;
‘
【
】
0
9
8
7
6
5
4
3
2
1
看到這個結(jié)果槽卫,不知道是否有小伙伴開始懷疑人生。關(guān)于這個問題胰蝠,我們先來打兩個斷點:
其實我們通過上面的結(jié)論歼培,當(dāng)斷點跳過 32 行的斷點之后震蒋,obj 是沒有被銷毀的,那么運行到 26 行的時候是否被銷毀了呢躲庄?我們試一下:
對查剖、運行到 26 行依然沒有被銷毀,那么問題來了:到底是在什么時候銷毀的呢噪窘?你可能會說:這不廢話么笋庄?那肯定是 26 行之后就銷毀了。現(xiàn)象是沒有錯的倔监,的確是在 26 行之后就銷毀了直砂。但是在揭穿真面目之前,還想再做一個實驗浩习,將代碼換成這樣的:
通過上面的介紹静暂,這里的打印 log 的順序是什么呢?這個得要很認(rèn)真的思考了谱秽。
洽蛀、
。
疟赊,
郊供、
;
‘
【
】
0
9
8
7
6
5
4
3
2
1
其實轉(zhuǎn)了一圈颂碘,恐怕都暈了吧锅劝。至此 autorelease 與大括號的關(guān)系讹剔,大家都有一個明確的理解了。以后別人問你的話劲赠,你還會怎么回答呢鼠证?
三峡竣、最后一公里
在揭穿之前,希望大家靜一靜量九,我們先來介紹一個小指令适掰,關(guān)于 lldb 的。很多時候我們總想看一個事件的調(diào)用棧荠列,我們可以使用這樣的代碼:
// 單元調(diào)用棧
NSLog(@"%@", [NSThread callStackSymbols]);
也可以直接在 lldb 中這樣使用:
以上都是可以的类浪,但是還有一個更簡單的, 直接在 lldb 處輸入 bt肌似, 然后回車即可费就。
我為什么要介紹這個呢?是因為很多的時候 Xcode 的這里是顯示不全的:
現(xiàn)在的 Xocde 很多有用的都被省略了川队,想看只能通過命令了力细。賺了一圈睬澡,也大了不少的斷點,就是沒有在 HGObject 中的 -dealloc 中打過斷點眠蚂,要想知道 autorelease 的對象是在什么時候被釋放的煞聪,直接在這個地方打個斷點看看不就可以了么?是啊逝慧、我的錯昔脯,把這里給忘記了。[勇于承認(rèn)錯誤笛臣,是我一直以來的光榮傳統(tǒng)美德]云稚。
那么我們就上面的那個狀態(tài),執(zhí)行以下 bt 指令捐祠,信息如下:
這張圖片的信息,粗略的介紹一下桑李。通過這張圖片能看到一個陌生既熟悉的關(guān)鍵字 AutoreleasePoolPage踱蛀,這又是什么?借用在一個微信群中某大神的一個解釋贵白,他的原話:autoreleasepool不是一個大棧率拒,是分一個一個固定大小的page,雙向鏈表連起來的禁荒。這里面所指的 page 應(yīng)該就是這個 AutoreleasePoolPage猬膨。具體這個 pop 是在什么時候被調(diào)用的,這與 NSRunloop 有關(guān)呛伴。
四勃痴、關(guān)于 __autoreleasing
可以先看一下這段代碼,想一下具體的打印順序是什么热康?
__weak id obj = nil;
{
{
{
{
// 打開下面的注釋, 分別看效果
/** __autoreleasing */ HGPerson* person = [[HGPerson alloc] init];
NSLog(@"%@", person);
obj = person;
NSLog(@"errr");
}
NSLog(@"end-1");
}
NSLog(@"end-2");
}
NSLog(@"end-3");
}
NSLog(@"end-4");
// 打印 obj 的值
NSLog(@"obj = %@", obj);
__autoreleasing 這個修飾符沛申,是相對于 ARC 才有效的。所以上面的代碼需要在 ARC 環(huán)境運行才能看到效果姐军。所以說:MRC 中有 autorelease 方法铁材, ARC 中有__autoreleasing 修飾符。
五奕锌、推薦
推薦一下我的其它文章:
- 1著觉、定時器集合:介紹了所有定時器的用法以及注意事項。
- 2惊暴、簡單易用 的 iOS 分類 : 主要是文本編輯與正則表達式的封裝饼丘。
- 3、神氣的 iOS 打包 :不知道為什么這篇文章的點擊率很高辽话,剛剛還有小伙伴在點贊葬毫≌蚧裕可能是因為標(biāo)題取得有點霸氣。
- 4贴捡、iOS單例的精心設(shè)計歷程 : 開發(fā)中應(yīng)該不會這么去設(shè)計一個單例忽肛,但是這里面介紹了很多的細節(jié),值得學(xué)習(xí)參考烂斋。