0、簡(jiǎn)單的說(shuō)一句
autorelease 已經(jīng)在 iOS 界叱咤風(fēng)云這么多年,現(xiàn)在網(wǎng)上也有很多類(lèi)似的文章,今天也來(lái)造個(gè)輪子。關(guān)于 autorelease 往往會(huì)出現(xiàn)這三個(gè)問(wèn)題:
- 1、是做什么的,有什么用
- 2、autorelease 后的對(duì)象,是在什么時(shí)候被銷(xiāo)毀的
- 3、__autoreleasing 的使用
當(dāng)然,很多的時(shí)候提問(wèn)者是直接問(wèn)第2個(gè)問(wèn)題,只要第2問(wèn)回答正確了,都沒(méi)有問(wèn)題了。如果是你,你會(huì)怎么回答。關(guān)于這個(gè)問(wèn)題,我會(huì)先給出常規(guī)的錯(cuò)誤答案,然后逐一解釋。
建議:本文主要是看過(guò)程、不要追求最終的答案。想看最后答案,直接看第三部分。
一、錯(cuò)誤回答
你是否會(huì)這樣的回答?!當(dāng)運(yùn)行跳出大括號(hào)之后,會(huì)給當(dāng)前自動(dòng)釋放池中發(fā)送過(guò) autorelease 消息的對(duì)象都發(fā)送一條 release 消息。貌似很多的小伙伴都會(huì)這么回答,遺憾的是 這個(gè)回答是錯(cuò)誤的,接下來(lái)會(huì)給出錯(cuò)誤的理由。十分的題、也就對(duì)了1分,在職場(chǎng)上回答不到8分的回答都是0分。
為什么是錯(cuò)誤的回答呢?因?yàn)榕c 大括號(hào) 沒(méi)有 多大 的關(guān)系。具體解釋?zhuān)?qǐng)看下文。
在開(kāi)始解釋之前,先定義一個(gè)Class,命名為:HGObject,定義如下:
#import <Foundation/Foundation.h>
@interface HGObject : NSObject
/** 名稱(chēng) */
@property (nonatomic, copy) NSString* name;
@end
====調(diào)皮的分割線(xiàn)====
#import "HGObject.h"
@implementation HGObject
- (void)dealloc {
NSLog(@" %@ 被釋放", _name);
[super dealloc];
}
@end
總的來(lái)說(shuō),就是定義了一個(gè)屬性 name,然后重寫(xiě)了一下 -dealloc 方法。從上面的帶來(lái)來(lái)看,我們接下來(lái)的演示中是在 MRC 環(huán)境下進(jìn)行的。
二、與大括號(hào)的關(guān)系
2.1 不會(huì)被釋放的情況

你看了之后,你會(huì)說(shuō):這肯定不會(huì)被釋放的,因?yàn)闆](méi)有再釋放池中。 對(duì)、很有道理,確實(shí)也是這樣,autorelease 必須要在池子中才會(huì)有效。那么我就再遷移到池子中,看一下效果。

結(jié)果還是一樣的:不會(huì)被釋放。看到這里的你是不是很激動(dòng),不信你就試試,如果釋放了記得告訴我。關(guān)于這種情況,也有這么解釋的:因?yàn)檫@里的 autoreleasepool 沒(méi)有被釋放,所以不會(huì)被釋放,這種解釋 100% 是錯(cuò)誤的解釋。但是如果上面的兩個(gè)地方將 autorelease 換成 release ,那么都是可以釋放的。

到這里,是不是就說(shuō)明了與大括號(hào)無(wú)關(guān)了?!
來(lái)簡(jiǎn)單的分析一下,在上面的兩種使用 autorelease 之后不會(huì)被釋放的情況。在上面的兩個(gè)地方,是一個(gè)非常不應(yīng)該的特例,因?yàn)槲覀円话悴粫?huì)在這個(gè)地方寫(xiě)代碼。但是有一點(diǎn)很情況的是:在上面的兩個(gè)地方都沒(méi)有被進(jìn)入 UIApplicationMain,在 UIApplicationMain 有一個(gè)特別重要的機(jī)制,叫:運(yùn)行循環(huán),美其名曰 NSRunLoop。如果說(shuō)能想到這一點(diǎn),那么就算是入門(mén)了。
看到上面的介紹,你會(huì)不會(huì)又會(huì)很激動(dòng)的說(shuō):對(duì)啊,我說(shuō)的就是在運(yùn)行循環(huán)中的與大括號(hào)有關(guān)的,在大括號(hào)結(jié)束之后就釋放了。 還想說(shuō)的是,這也是錯(cuò)誤的。是與大括號(hào)有關(guān)系,但是并不是 那 一層的關(guān)系。
2.2 與大括號(hào)的那一層關(guān)系
先看一下這個(gè)代碼:

關(guān)于上面的代碼,當(dāng)我點(diǎn)擊屏幕的時(shí)候,會(huì)打印
兩個(gè) log 日志信息, 是先打印 testAutorelease 執(zhí)行完畢 呢,還是先打印 obj 對(duì)象 被釋放。思考一下、把你的答案放在心中,稍后公布答案。
、
。
,
、
;
‘
【
】
0
9
8
7
6
5
4
3
2
1

看到這個(gè)結(jié)果,不知道是否有小伙伴開(kāi)始懷疑人生。關(guān)于這個(gè)問(wèn)題,我們先來(lái)打兩個(gè)斷點(diǎn):

其實(shí)我們通過(guò)上面的結(jié)論,當(dāng)斷點(diǎn)跳過(guò) 32 行的斷點(diǎn)之后,obj 是沒(méi)有被銷(xiāo)毀的,那么運(yùn)行到 26 行的時(shí)候是否被銷(xiāo)毀了呢?我們?cè)囈幌拢?br>

對(duì)、運(yùn)行到 26 行依然沒(méi)有被銷(xiāo)毀,那么問(wèn)題來(lái)了:到底是在什么時(shí)候銷(xiāo)毀的呢?你可能會(huì)說(shuō):這不廢話(huà)么?那肯定是 26 行之后就銷(xiāo)毀了?,F(xiàn)象是沒(méi)有錯(cuò)的,的確是在 26 行之后就銷(xiāo)毀了。但是在揭穿真面目之前,還想再做一個(gè)實(shí)驗(yàn),將代碼換成這樣的:

通過(guò)上面的介紹,這里的打印 log 的順序是什么呢?這個(gè)得要很認(rèn)真的思考了。
、
。
,
、
;
‘
【
】
0
9
8
7
6
5
4
3
2
1

其實(shí)轉(zhuǎn)了一圈,恐怕都暈了吧。至此 autorelease 與大括號(hào)的關(guān)系,大家都有一個(gè)明確的理解了。以后別人問(wèn)你的話(huà),你還會(huì)怎么回答呢?
三、最后一公里
在揭穿之前,希望大家靜一靜,我們先來(lái)介紹一個(gè)小指令,關(guān)于 lldb 的。很多時(shí)候我們總想看一個(gè)事件的調(diào)用棧,我們可以使用這樣的代碼:
// 單元調(diào)用棧
NSLog(@"%@", [NSThread callStackSymbols]);
也可以直接在 lldb 中這樣使用:

以上都是可以的,但是還有一個(gè)更簡(jiǎn)單的, 直接在 lldb 處輸入 bt, 然后回車(chē)即可。
我為什么要介紹這個(gè)呢?是因?yàn)楹芏嗟臅r(shí)候 Xcode 的這里是顯示不全的:

現(xiàn)在的 Xocde 很多有用的都被省略了,想看只能通過(guò)命令了。賺了一圈,也大了不少的斷點(diǎn),就是沒(méi)有在 HGObject 中的 -dealloc 中打過(guò)斷點(diǎn),要想知道 autorelease 的對(duì)象是在什么時(shí)候被釋放的,直接在這個(gè)地方打個(gè)斷點(diǎn)看看不就可以了么?是啊、我的錯(cuò),把這里給忘記了。[勇于承認(rèn)錯(cuò)誤,是我一直以來(lái)的光榮傳統(tǒng)美德]。
那么我們就上面的那個(gè)狀態(tài),執(zhí)行以下 bt 指令,信息如下:

這張圖片的信息,粗略的介紹一下。通過(guò)這張圖片能看到一個(gè)陌生既熟悉的關(guān)鍵字 AutoreleasePoolPage,這又是什么?借用在一個(gè)微信群中某大神的一個(gè)解釋?zhuān)脑?huà):autoreleasepool不是一個(gè)大棧,是分一個(gè)一個(gè)固定大小的page,雙向鏈表連起來(lái)的。這里面所指的 page 應(yīng)該就是這個(gè) AutoreleasePoolPage。具體這個(gè) pop 是在什么時(shí)候被調(diào)用的,這與 NSRunloop 有關(guān)。
四、關(guān)于 __autoreleasing
可以先看一下這段代碼,想一下具體的打印順序是什么?
__weak id obj = nil;
{
{
{
{
// 打開(kāi)下面的注釋, 分別看效果
/** __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 這個(gè)修飾符,是相對(duì)于 ARC 才有效的。所以上面的代碼需要在 ARC 環(huán)境運(yùn)行才能看到效果。所以說(shuō):MRC 中有 autorelease 方法, ARC 中有__autoreleasing 修飾符。
五、推薦
推薦一下我的其它文章:
- 1、定時(shí)器集合:介紹了所有定時(shí)器的用法以及注意事項(xiàng)。
- 2、簡(jiǎn)單易用 的 iOS 分類(lèi) : 主要是文本編輯與正則表達(dá)式的封裝。
- 3、神氣的 iOS 打包 :不知道為什么這篇文章的點(diǎn)擊率很高,剛剛還有小伙伴在點(diǎn)贊??赡苁且?yàn)闃?biāo)題取得有點(diǎn)霸氣。
- 4、iOS單例的精心設(shè)計(jì)歷程 : 開(kāi)發(fā)中應(yīng)該不會(huì)這么去設(shè)計(jì)一個(gè)單例,但是這里面介紹了很多的細(xì)節(jié),值得學(xué)習(xí)參考。