這篇文章其實(shí)是深入內(nèi)存管理:從所有權(quán)修飾符開(kāi)始的補(bǔ)充。因?yàn)橛捎?code>__autoreleasing的試驗(yàn)過(guò)于多献联,都寫(xiě)在上一篇文章中會(huì)使得文章篇幅結(jié)構(gòu)很難看蛮原,所以在這里新建一篇文章來(lái)記錄。
方法介紹
下面需要介紹兩個(gè)方法:
1._objc_rootRetainCount(id obj)
方法淮阐,作用是返回obj的引用計(jì)數(shù)蔓腐。
2._objc_autoreleasePoolPrint()
方法矩乐,作用是打印當(dāng)前的自動(dòng)釋放池對(duì)象。
使用方法:
直接定義在類(lèi)中就可以使用
extern uintptr_t _objc_rootRetainCount(id obj);
extern void _objc_autoreleasePoolPrint(void);
試驗(yàn)開(kāi)始
一合住、基礎(chǔ)試驗(yàn)
下面有三個(gè)小實(shí)驗(yàn)绰精,為了證明結(jié)論:
在取得非自己生成并持有的對(duì)象時(shí),編譯器會(huì)默認(rèn)把對(duì)象注冊(cè)到自動(dòng)釋放池中透葛。
也就是說(shuō):
編譯器為判斷方法名是否是以
alloc/new/copy/mutableCopy
開(kāi)頭笨使,如果不是,就自動(dòng)將返回的對(duì)象注冊(cè)到池子中僚害。
1. 直接alloc方法初始化
代碼:
id __weak obj0;
{
id obj1 = [[NSMutableArray alloc] init];
obj0 = obj1;
NSLog(@"%p", obj0);
NSLog(@"%lu", _objc_rootRetainCount(obj0));
_objc_autoreleasePoolPrint();
}
NSLog(@"obj0-1-%@", obj0);
實(shí)驗(yàn)結(jié)果:
程序到最后一行時(shí)輸出為空硫椰。
輸出:
分析:
這里其實(shí)很簡(jiǎn)單繁调,編譯器的模擬代碼為
id obj1 = objc_msgSend(NSMutableArray, @selector(alloc));
objc_msgSend(obj1, @selector(init));
objc_release(obj1);
當(dāng)變量obj1的作用域消失時(shí),編譯器會(huì)自動(dòng)插入objc_release(obj1);
靶草。這里并沒(méi)有涉及到自動(dòng)釋放池蹄胰。所以array對(duì)象會(huì)被自動(dòng)銷(xiāo)毀。
2. 用array方法初始化
代碼
id __weak obj0;
{
id obj2 = [NSMutableArray array];
obj0 = obj2;
NSLog(@"%p", obj2);
NSLog(@"%lu", _objc_rootRetainCount(obj2));
_objc_autoreleasePoolPrint();
}
NSLog(@"obj0-2-%@", obj0);
實(shí)驗(yàn)結(jié)果:
分析:
這里發(fā)現(xiàn)obj2的引用計(jì)數(shù)為2奕翔,再看自動(dòng)釋放池最后一個(gè)對(duì)象類(lèi)型為__NSArrayM
裕寨,正是obj2持有的對(duì)象。說(shuō)明該對(duì)象已經(jīng)加入到了自動(dòng)釋放池中派继,所以最后輸出有值宾袜。那么這個(gè)對(duì)象什么時(shí)候釋放呢,我們后面說(shuō)驾窟。
3. 用array方法初始化 并自己添加池子
代碼:
@autoreleasepool {
id obj3 = [NSMutableArray array];
obj0 = obj3;
NSLog(@"%p", obj3);
NSLog(@"%lu", _objc_rootRetainCount(obj3));
_objc_autoreleasePoolPrint();
}
NSLog(@"obj0-3-%@", obj0);
實(shí)驗(yàn)結(jié)果:
分析:
雖然這里和實(shí)驗(yàn)2一樣被放入到了池子中庆猫,但是最后打印還是為空。說(shuō)明在退出池子后绅络,對(duì)象被銷(xiāo)毀了月培。
4.總結(jié)
以上三個(gè)實(shí)驗(yàn)驗(yàn)證了之前提出的結(jié)論。
下面以第三個(gè)實(shí)驗(yàn)作為例子分析代碼:
@autoreleasepool {
// [NSMutableArray array]返回的對(duì)象會(huì)被默認(rèn)加入到池子中
// 對(duì)象的引用計(jì)數(shù)為1
// obj3默認(rèn)為_(kāi)_strong恩急,強(qiáng)引用array對(duì)象
// array對(duì)象的引用計(jì)數(shù)為2
id obj3 = [NSMutableArray array];
// obj0對(duì)__weak 因此對(duì)象引用計(jì)數(shù)不變
obj0 = obj3;
NSLog(@"%p", obj3);
NSLog(@"%lu", _objc_rootRetainCount(obj3));
_objc_autoreleasePoolPrint();
}
// obj3的作用域結(jié)束杉畜,釋放對(duì)象 計(jì)數(shù)-1
// 池子結(jié)束 池子中的對(duì)象要被釋放 計(jì)數(shù)-1
// 對(duì)象計(jì)數(shù)為0 因此銷(xiāo)毀
// 輸出為空
NSLog(@"obj0-3-%@", obj0);
二、再次驗(yàn)證試驗(yàn)一
下面驗(yàn)證自己定義的方法是否也可以遵守以上結(jié)論假栓。
1.不以alloc等開(kāi)頭的方法
代碼:
{
id obj5 = [[self class] Object];
obj0 = obj5;
NSLog(@"%p", obj0);
NSLog(@"%lu", _objc_rootRetainCount(obj0));
_objc_autoreleasePoolPrint();
}
NSLog(@"obj0-5-%@", obj0);
+ (id)Object
{
id array = [[NSMutableArray alloc] init];
return array;
}
輸出:
分析:
最后輸出沒(méi)有問(wèn)題寻行,確實(shí)是加入到了池子中。但是為什么這里的計(jì)數(shù)是3呢匾荆??杆烁?
2.以alloc等開(kāi)頭的方法
代碼:
{
id obj6 = [[self class] allocObject];
obj0 = obj6;
NSLog(@"%p", obj0);
NSLog(@"%lu", _objc_rootRetainCount(obj0));
_objc_autoreleasePoolPrint();
}
NSLog(@"obj0-6-%@", obj0);
+ (id)allocObject
{
id array = [[NSMutableArray alloc] init];
return array;
}
輸出:
分析:
這里的引用計(jì)數(shù)變成2了牙丽,但是沒(méi)有加入到池子中,且最后輸出為空兔魂。說(shuō)明這里多出的1的引用計(jì)數(shù)來(lái)自+ (id)allocObject
方法烤芦。
3.總結(jié)
自定義的方法仍然可以說(shuō)明實(shí)驗(yàn)一的結(jié)論。
三析校、何時(shí)釋放
下面定義一個(gè)屬性@property (nonatomic, weak) id obj;
构罗,并在- (void)viewDidLoad
中賦值。
- (void)viewDidLoad
{
[super viewDidLoad];
id obj = [NSMutableArray array];
self.obj = obj;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
NSLog(@"%p", self.obj);
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
NSLog(@"%p", self.obj);
}
輸出:
2017-04-28 11:23:54.829 MRCTest[57782:5898069] 0x6000000523f0
2017-04-28 11:23:54.832 MRCTest[57782:5898069] 0x0
為什么在viewWillAppear
中還存在而到了viewDidAppear
中就為空了智玻?我猜測(cè)是否是執(zhí)行完viewWillAppear
后遂唧,自動(dòng)釋放池銷(xiāo)毀了,導(dǎo)致array對(duì)象也銷(xiāo)毀了吊奢。
其實(shí)原因是viewDidLoad
和viewWillAppear
是在同一個(gè)RunLoop中調(diào)用的盖彭,而viewDidAppear
與他們不是同一個(gè),因此當(dāng)RunLoop一圈結(jié)束時(shí),池子被銷(xiāo)毀召边,里面的對(duì)象也自然被銷(xiāo)毀了铺呵。
最后
其實(shí)我想寫(xiě)一點(diǎn)__strong
以及__autoreleasing
底層代碼的,但是因?yàn)樽约阂矝](méi)有完全理解隧熙,所以還是不亂寫(xiě)了片挂。