關(guān)于我的倉(cāng)庫(kù)
- 這篇文章是我為面試準(zhǔn)備的iOS基礎(chǔ)知識(shí)學(xué)習(xí)中的一篇
- 我將準(zhǔn)備面試中找到的所有學(xué)習(xí)資料,寫(xiě)的Demo,寫(xiě)的博客都放在了這個(gè)倉(cāng)庫(kù)里iOS-Engineer-Interview
- 歡迎star????
- 其中的博客在簡(jiǎn)書(shū),CSDN都有發(fā)布
- 博客中提到的相關(guān)的代碼Demo可以在倉(cāng)庫(kù)里相應(yīng)的文件夾里找到
前言
- ARC使用了四種所有權(quán)修飾符對(duì)引用計(jì)數(shù)進(jìn)行管理,使得不再需要手動(dòng)操作retain,release
- 理解ARC規(guī)則,使得對(duì)于內(nèi)存管理的思考方式更進(jìn)一步,以O(shè)C的方式來(lái)思考內(nèi)存管理是理解的難點(diǎn)
- 由于markdown渲染的鍋,本文忽略了一些雙下劃線
準(zhǔn)備工作
閱讀《Objective-C 高級(jí)編程》中的p.29 ~ 78
回顧下內(nèi)存管理四大原則是不是牢牢記住了
內(nèi)存管理四大原則
- 自己生成的對(duì)象,自己持有
- 非自己生成的對(duì)象,自己也能持有
- 不再需要自己持有的對(duì)象時(shí)釋放
- 非自己持有的對(duì)象無(wú)法釋放
ARC規(guī)則(p.29 ~ p.65)
__strong修飾符
- __strong是默認(rèn)修飾符
id obj = [[NSObject alloc] init];
//id __strong obj = [[NSObject alloc] init];
NSObject *obj = [[NSObject alloc] init];
//NSObject __strong *obj = [[NSObject alloc] init];
補(bǔ)充知識(shí):id的本質(zhì)
- id是struct objc_object結(jié)構(gòu)體指針,可以指向任何OC對(duì)象,當(dāng)然不包括NSInteger等,因?yàn)檫@些數(shù)據(jù)類型不是OC對(duì)象。另外OC的基類,其實(shí)不僅僅就NSObject一個(gè),雖然NSObject是絕大數(shù)OC對(duì)象的基類,但是還有個(gè)NSProxy虛類。所以不能說(shuō)id類型和NSObject *是等價(jià)的。
- id類型的實(shí)例在編譯階段不會(huì)做類型檢測(cè),會(huì)在運(yùn)行時(shí)確定,所以id類型是運(yùn)行時(shí)的動(dòng)態(tài)類型。類NSObject的實(shí)例會(huì)編譯期要做編譯檢查,保證指針指向是其NSObject類或其子類,當(dāng)然實(shí)例的具體類型要在運(yùn)行期確定,這也是iOS的多態(tài)的體現(xiàn)。
- 記住:id本身就是個(gè)指針【不記住這一點(diǎn)的話,后面看到__autoreleasing可能會(huì)像我一樣崩潰了??】
超出變量作用域 = 廢棄
- 這里要開(kāi)始轉(zhuǎn)換思維模式了,比如這樣一句代碼id obj = [[NSObject alloc] init];
- 我們這里有兩個(gè)東西,一個(gè)是obj,它是一個(gè)指針,指向生成對(duì)象;同時(shí),它隱式使用了__strong,它也就持有了該對(duì)象,成為對(duì)象持有者【也就是說(shuō)對(duì)象的引用計(jì)數(shù)+1了,還是強(qiáng)調(diào)一遍,把引用計(jì)數(shù)作為輔助手段去理解,還是要從持有與否的角度去看待內(nèi)存管理】
- 這里我們看p.31最下面這個(gè)obj失效的例子
- 記住這個(gè)反應(yīng)鏈:
- obj超出作用域,obj失效,不再持有該對(duì)象
- 失去強(qiáng)引用,對(duì)象不在被持有【也就是引用計(jì)數(shù)從1到0,自動(dòng)dealloc】
- 對(duì)象被釋放
- 這里一定要分清,持有者是持有者,對(duì)象是對(duì)象,兩者不是一個(gè)東西
__strong對(duì)象相互賦值
- 書(shū)上這個(gè)例子可能注釋的還不是很好理解,我這里直接放一張表格,研究三個(gè)obj分別持有誰(shuí)
| 語(yǔ)句 | obj0 | obj1 | obj2 |
|---|---|---|---|
| id __strong obj0 = [[NSObject alloc] init];//生成對(duì)象A | A | ||
| id __strong obj1 = [[NSObject alloc] init];//生成對(duì)象B | A | B | |
| id __strong obj2 = nil; | A | B | nil |
| obj0 = obj1;//obj0強(qiáng)引用對(duì)象B;而對(duì)象A不再被ojb0引用,被廢棄 | B | B | nil |
| obj2 = obj0;//obj2強(qiáng)引用對(duì)象B(現(xiàn)在obj0,ojb1,obj2都強(qiáng)引用對(duì)象B) | B | B | B |
| obj1 = nil;//obj1不再?gòu)?qiáng)引用對(duì)象B | B | nil | B |
| obj0 = nil;//obj0不再?gòu)?qiáng)引用對(duì)象B | nil | nil | B |
| obj2 = nil;//obj2不再?gòu)?qiáng)引用對(duì)象B,不再有任何強(qiáng)引用引用對(duì)象B,對(duì)象B被廢棄 | nil | nil | nil |
- 賦值的本質(zhì)是強(qiáng)引用的轉(zhuǎn)變,這也可以幫助我們理解為什么要引入這一套規(guī)則,我們不能也不用去直接對(duì)對(duì)象進(jìn)行操作管理,使用指針和引用計(jì)數(shù)安全,有效的進(jìn)行使用
方法參數(shù)中使用__strong
- 到這里還是重復(fù)確認(rèn)下,__strong就是默認(rèn)的修飾符
- 以及這句話很關(guān)鍵廢棄Test對(duì)象的同時(shí),Test對(duì)象的obj_成員也被廢除
- 也就是說(shuō)成員變量的生存周期是與對(duì)象同步的
__strong導(dǎo)致的循環(huán)引用
- 前面所有的強(qiáng)調(diào)加黑的文字都是為了能理解這里的循環(huán)引用
補(bǔ)充知識(shí):內(nèi)存泄漏
- 簡(jiǎn)單來(lái)說(shuō),內(nèi)存泄漏就是在內(nèi)存該被釋放的時(shí)候沒(méi)有釋放,導(dǎo)致內(nèi)存被浪費(fèi)使用了
- 內(nèi)存泄漏在iOS開(kāi)發(fā)中輕則影響性能,重則導(dǎo)致crash
賦值階段
- 這里還是放一張表格,說(shuō)明這兩個(gè)test以及其成員持有誰(shuí)
| 語(yǔ)句 | test0 | test0.obj | test1 | test1.obj |
|---|---|---|---|---|
| id test0 = [[Test alloc] init];//生成TestA | TestA | |||
| id test1 = [[Test alloc] init];//生成TestB | TestA | TestB | ||
| [test0 setObject:test1]; | TestA | TestB | TestB | TestA |
| [test1 setObject:test0]; | TestA | TestB | TestB | TestA |
失效階段
- 這里為了加強(qiáng)理解,直接把各自的引用計(jì)數(shù),持有對(duì)象都列出來(lái)
| What happen | TestA引用計(jì)數(shù) | TestA持有者 | TestB引用計(jì)數(shù) | TestB持有者 |
|---|---|---|---|---|
| 初始狀態(tài) | 2 | test0,test1.obj | 2 | test0.obj,test1 |
| test0超出作用域 | 1 | test1.obj | 2 | test0.obj,test1 |
| test1超出作用域 | 1 | test1.obj | 1 | test0.obj |
為什么test0失效的時(shí)候,obj_依然存在
這里還是用引用計(jì)數(shù)的方式來(lái)思考比較好
只有當(dāng)某個(gè)對(duì)象的持有者都被釋放了,該對(duì)象才會(huì)被dealloc,而這里的test0,test1僅僅只是指針而已
這點(diǎn)非常重要,如果在你的腦海里想的是test0被釋放,他下面的obj_自然不復(fù)存在了,請(qǐng)牢記test0僅僅是一個(gè)指針,是一個(gè)對(duì)象的持有者,不是對(duì)象本身
造成結(jié)果
我要dealloc TestA
- 我們看,testA dealloc不了,因?yàn)閠est1.obj持有了testA
- 而想要廢除test1.obj,就如我們上面介紹的,廢棄Test對(duì)象的同時(shí),Test對(duì)象的obj_成員也被廢除
- 而test1是TestB內(nèi)存里的成員變量
- 一句話,想dealloc TestA,必須先dealloc TestB才行
我要dealloc TestB
- 我們看,testB dealloc不了,因?yàn)閠est0.obj持有了testB
- 而想要廢除test0.obj,就如我們上面介紹的,廢棄Test對(duì)象的同時(shí),Test對(duì)象的obj_成員也被廢除
- 而test0是TestA內(nèi)存里的成員變量
- 一句話,想dealloc TestB,必須先dealloc TestA才行
對(duì)自身的強(qiáng)引用
- 對(duì)自身的強(qiáng)引用和上面其實(shí)一個(gè)意思,test,test.obj同時(shí)持有了對(duì)象,test超出定義域release,對(duì)象引用計(jì)數(shù)-1
- 而test.obj遇到了和上面一樣的難題,如果還是想不通發(fā)生了什么,請(qǐng)把上面兩段話再認(rèn)真看看
__weak修飾符
- __weak持有弱引用
- 如果對(duì)于strong修飾符理解OK的話,這個(gè)weak修飾符其實(shí)很好懂
生成__weak的持有者
id __weak obj = [[NSObject alloc] init];
//這里如果直接使用__weak obj來(lái)持有對(duì)象,由于這里是弱引用,引用計(jì)數(shù)不會(huì)加一,對(duì)象隨時(shí)可能會(huì)被dealloc
//后面會(huì)講到,其實(shí)內(nèi)部是用__autoreleasing來(lái)維持該對(duì)象不被dealloc
id __strong obj0 = [[NSObject alloc] init];
id __weak obj1 = obj0;
//在這里,obj0先強(qiáng)持有該對(duì)象,給它引用計(jì)數(shù)+1,防止其dealloc
//之后再讓obj1去弱持有該對(duì)象,達(dá)成我們需要的目的
使用__weak的好處
- 如果對(duì)于前面循環(huán)引用的原因研究理解到位了,應(yīng)該就能明白為什么__weak能避免循環(huán)引用
- __weak不會(huì)增加引用計(jì)數(shù),相應(yīng)的對(duì)象該dealloc就會(huì)dealloc,其中的持有者自然而然就會(huì)被release
- 這里可以思考下,如果一個(gè)弱引用,一個(gè)強(qiáng)引用,這樣子的相互引用會(huì)導(dǎo)致循環(huán)引用嗎?
__autoreleasing修飾符
與MRC時(shí)比較
- 先回憶一下,MRC中autorelease的使用方法
- 生成并持有NSAutoreleasePool對(duì)象。
- 調(diào)用已分配對(duì)象的autorelease方法。【將對(duì)象注冊(cè)到pool中】
- 廢棄NSAutoreleasePool對(duì)象。【pool執(zhí)行drain廢除,其中的對(duì)象也跟著release】
- 我們會(huì)發(fā)現(xiàn),這里出現(xiàn)兩個(gè)部分,一個(gè)pool,一個(gè)對(duì)象
- 于此相對(duì)的,__autoreleasing同樣分成兩塊

64149CAE-5DF0-44CC-9E67-3A10081A9DC3
自動(dòng)調(diào)用
- MRC中有介紹,像array這樣的方法,生成的對(duì)象不是由自己持有的,其中就是靠__autoreleasing修飾符去實(shí)現(xiàn)
- 當(dāng)編譯器檢測(cè)到這樣的方法命名后,就會(huì)自動(dòng)加上__autoreleasing修飾符
- 那么這里有個(gè)注意點(diǎn)就是,strong才是默認(rèn)的修飾符,我們?nèi)绻胹trong修飾符去接收的對(duì)象,當(dāng)其超出作用域的時(shí)候,strong修飾符先失效,走出@autoreleasepool塊后,__autoreleasing修飾符失效
weak修飾符與autoreleasing修飾符
- 如同上面提到的,weak修飾符的實(shí)現(xiàn)要借助autoreleasing修飾符
id __weak obj1 = obj0;
NSLog(@"class = %@",[obj1 class]);
id __weak obj1 = obj0;
id __autoreleasing tmp = obj1;
NSLog(@"class = %@",[tmp class]);//實(shí)際訪問(wèn)的是注冊(cè)到自動(dòng)釋放池的對(duì)象
- 由于weak不會(huì)增加引用計(jì)數(shù),對(duì)象難以維持,所以要通過(guò)__autoreleasing來(lái)維護(hù)
- 在使用附有weak修飾符的變量時(shí)就必定要使用到autoreleasing修飾符
autoreleasing修飾符無(wú)處不在
牢記:只有作為alloc/new/copy/mutableCopy方法的返回值而渠道對(duì)象時(shí),能夠自己生成并持有對(duì)象,其他情況都是"取得非自己生成并持有的對(duì)象",換句話說(shuō),就輪到我們的autoreleasing修飾符上場(chǎng)了
具體ARC規(guī)則
規(guī)則
- 不能使用retain/release/retainCount/autorelease
- 不能使用NSAllocateObject/NSDeallocateObject
- 必須遵守內(nèi)存管理的方法名規(guī)則
- 不要顯式調(diào)用dealloc
- 使用@autorelease塊代替NSAutoreleasePool
- 不能使用區(qū)域(NSZone)
- 對(duì)象型變量不能作為C語(yǔ)言結(jié)構(gòu)體的成員
- 顯式轉(zhuǎn)換id和void*
dealloc
- 重寫(xiě)dealloc方法時(shí)不需要寫(xiě)[super dealloc]
- dealloc無(wú)法釋放不屬于該對(duì)象的一些東西,需要我們重寫(xiě)時(shí)加上去,例如
- 通知的觀察者,或KVO的觀察者
- 對(duì)象強(qiáng)委托/引用的解除(例如XMPPMannerger的delegateQueue)
- 做一些其他的注銷之類的操作(關(guān)閉程序運(yùn)行期間沒(méi)有關(guān)閉的資源)
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];//移除通知觀察者
[[XMPPManager sharedManager] removeFromDelegateQueue:self];//移除委托引用
[ [MyClass shareInstance] doSomething ]//其他操作
}
__bridge
-
__bridge可以實(shí)現(xiàn)Objective-C與C語(yǔ)言變量 和 Objective-C與Core Foundation對(duì)象之間的互相轉(zhuǎn)換 -
__bridge不會(huì)改變對(duì)象的持有狀況,既不會(huì)retain,也不會(huì)release -
__bridge轉(zhuǎn)換需要慎重分析對(duì)象的持有情況,稍不注意就會(huì)內(nèi)存泄漏 -
__bridge_retained用于將OC變量轉(zhuǎn)換為C語(yǔ)言變量 或 將OC對(duì)象轉(zhuǎn)換為Core Foundation對(duì)象 -
__bridge_retained類似于retain,“被轉(zhuǎn)換的變量”所持有的對(duì)象在變量賦值給“轉(zhuǎn)換目標(biāo)變量”后持有該對(duì)象 -
__bridge_transfer用于將C語(yǔ)言變量轉(zhuǎn)換為OC變量 或 將Core Foundation對(duì)象轉(zhuǎn)換為OC對(duì)象 -
__bridge_transfer類似于release,“被轉(zhuǎn)換的變量”所持有的對(duì)象在變量賦值給“轉(zhuǎn)換目標(biāo)變量”后隨之釋放
屬性聲明與所有權(quán)修飾符

D1980B5B-D29C-4B3B-8FC0-121567F38F0C
ARC實(shí)現(xiàn)(p.65 ~ p.78)
說(shuō)明
- 這是一張最新(750.1)objc4庫(kù).mm文件列表

C10E595E-A479-433F-AE43-9920A37275D1
- 可以看到,這個(gè)里甚至沒(méi)有作者提到的objc-arr.mm這個(gè)文件了
- 還是再?gòu)?qiáng)調(diào)一次,objc4庫(kù)Apple一直在不停的更新,所以書(shū)里講到的源碼實(shí)現(xiàn)可能都和目前最新的脫軌了
- 所以看書(shū)的時(shí)候還是以了解為主,想理解現(xiàn)在真正的源碼實(shí)現(xiàn)方式,當(dāng)然還是要啃最新源碼
__strong修飾符實(shí)現(xiàn)
//自己持有
{
id __strong obj = [NSObject alloc] init];//obj持有對(duì)象
}
id obj = objc_mesgSend(NSObject, @selector(alloc));
objc_msgSend(obj,@selector(init));
objc_release(obj);//超出作用域,釋放對(duì)象
//非自己持有
{
id __strong obj = [NSMutableArray array];
}
id obj = objc_msgSend(NSMutableArray, @selector(array));
objc_retainAutoreleasedReturnValue(obj);//objc_retainAutoreleasedReturnValue的作用:持有對(duì)象,將對(duì)象注冊(cè)到autoreleasepool并返回。
objc_release(obj);
+ (id)array
{
return [[NSMutableArray alloc] init];
}
+ (id)array
{
id obj = objc_msgSend(NSMutableArray, @selector(alloc));
objc_msgSend(obj,, @selector(init));
return objc_autoreleaseReturnValue(obj);//objc_autoreleaseReturnValue:返回注冊(cè)到autoreleasepool的對(duì)象。
}
objc_retainAutoreleasedReturnValue與objc_autoreleaseReturnValue
兩個(gè)不一定
- objc_retainAutoreleasedReturnValue不一定非要持有注冊(cè)到pool里的對(duì)象
- objc_autoreleaseReturnValue不一定非要注冊(cè)到pool中
解釋
-
書(shū)上的圖說(shuō)的很清楚
205617F1-C561-4157-BB78-B1EE275F7040 -
將這張圖分成兩部分去看
-
首先是左側(cè)三個(gè)灰色箭頭代表的普通流程
- object注冊(cè)到pool中
- 在pool中找到對(duì)象,返回
-
然后是右側(cè)三個(gè)黑色箭頭構(gòu)成的優(yōu)化情況,就是當(dāng)objc_autoreleaseReturnValue后直接objc_retainAutoreleasedReturnValue的情況
- 直接就是objc_autoreleaseReturnValue獲取對(duì)象
- objc_retainAutoreleasedReturnValue持有返回的對(duì)象
這樣子,跳過(guò)了中間的pool這個(gè)中轉(zhuǎn)站,實(shí)現(xiàn)優(yōu)化
-
__weak修飾符實(shí)現(xiàn)
objc_storeWeak
- objc_storeWeak(&obj1, obj)會(huì)使用weak表來(lái)存儲(chǔ)使用weak修飾符的變量的地址
- weak表同樣也是哈希表,其key是對(duì)象的地址,value是附有weak修飾符變量的地址
- 如果第二參數(shù)是0的話,就把第一參數(shù)里的地址從weak表中刪除
注冊(cè)到autoreleasepool
id obj1;
objc_initWeak(&obj1,obj);//初始化附有__weak的變量
id tmp = objc_loadWeakRetained(&obj1);//取出附有__weak修飾符變量所引用的對(duì)象并retain
objc_autorelease(tmp);//將對(duì)象注冊(cè)到autoreleasepool中
objc_destroyWeak(&obj1);//釋放附有__weak的變量
- 這里注意,每次都是訪問(wèn)時(shí),注冊(cè)到pool里,每訪問(wèn)一次注冊(cè)一次
- 因此書(shū)上推薦再將其賦值給一個(gè)strong
__autoreleasing修飾符實(shí)現(xiàn)
id pool = objc_autoreleasePoolPush();//pool入棧
id obj = objc_msgSend(NSObject, @selector(alloc));
objc_msgSend(obj, @selector(init));
objc_autorelease(obj);
objc_autoreleasePoolPop(pool);//pool出棧
2019.7.22 更新:關(guān)于weak修飾符的一些打印實(shí)驗(yàn)
//在ARC中我們可以使用__bridge來(lái)查看應(yīng)用計(jì)數(shù)
NSObject *obj0 = [[NSObject alloc] init];
printf("retain count = %ld\n",CFGetRetainCount((__bridge CFTypeRef)(obj0)));
NSObject * __weak obj1 = obj0;
printf("retain count = %ld\n",CFGetRetainCount((__bridge CFTypeRef)(obj1)));
printf("retain count = %ld\n",CFGetRetainCount((__bridge CFTypeRef)(obj0)));
// retain count = 1
// retain count = 2
// retain count = 1
- 這里我們可以對(duì)與weak修飾符有更深的了解,就如書(shū)上p.46頁(yè)上說(shuō)的
id __weak obj1 = obj0;
NSLog(@"class = %@",[obj1 class]);
id __weak obj1 = obj0;
id __autoreleasing tmp = obj1;
NSLog(@"class = %@",[tmp class]);//實(shí)際訪問(wèn)的是注冊(cè)到自動(dòng)釋放池的對(duì)象
- 這也是為什么我們第二次打印出來(lái)的retain count = 2,因?yàn)槲覀兊韧趯?duì)象注冊(cè)到了autoreleasepool中,因此引用計(jì)數(shù)+1
- 但是我們第三個(gè)打印又變回了1,這說(shuō)明兩件事
- 不是一使用weak修飾符就會(huì)直接注冊(cè)到pool中,是當(dāng)你訪問(wèn)的時(shí)候才會(huì)生成一個(gè)__autoreleasing tmp,這也是為什么作者取變量名為tmp
- 而接下來(lái)就變回1,說(shuō)明當(dāng)訪問(wèn)完之后就會(huì)直接釋放,等同于release了,導(dǎo)致引用計(jì)數(shù)
- 總結(jié)一下就是 ,weak修飾符之所以要生成tmp,只是為了防止該對(duì)象無(wú)人引用,會(huì)直接dealloc,因此使用一個(gè)tmp維護(hù)住它,當(dāng)訪問(wèn)結(jié)束后,這個(gè)也就釋放了
2019.7.25晚 更新:《Effective Objective-C 2.0》中的ARC
- 絕不應(yīng)該說(shuō)引用計(jì)數(shù)一定是幾,只能說(shuō)執(zhí)行的操作是遞增了該計(jì)數(shù)還是遞減了該計(jì)數(shù)
p.121 stringValue
+ (NSString *)stringValue {
NSString *str = [[NSString alloc] initWithFormat:@"I am this:%@", self];
return str;
}
NSString *str1 = [NSString stringValue];
- 這里提到了返回的str對(duì)象其保留計(jì)數(shù)比期望值要多1(+1 retain count)
- 我們來(lái)試著解讀為什么
- 個(gè)人認(rèn)為這里的意思是stringValue中包裝了一個(gè)init方法,這個(gè)init方法會(huì)使該對(duì)象引用計(jì)數(shù)+1,然后當(dāng)我們返回時(shí),我們需要接收這個(gè)返回值,導(dǎo)致引用計(jì)數(shù)又加了一,也就相當(dāng)于
NSString *str = [NSString alloc] initWithFormat:@"I am this:%@", self];
NSString *str1 = str;//str1就是接收者,使得該對(duì)象又被retain了一次
- 還是很牽強(qiáng),但又覺(jué)得只能這么理解
p.124 三個(gè)演示ARC的例子
- 這個(gè)方法以"new"開(kāi)頭,既然"person"已經(jīng)使得引用計(jì)數(shù)+1了【因?yàn)閍lloc】,那么我們不需要做任何操作了,直接返回就行;也就是說(shuō),以alloc開(kāi)頭的方法,只要保證返回的對(duì)象引用計(jì)數(shù)+1了就行
- 該方法不是以那幾個(gè)開(kāi)頭,因此返回時(shí)自動(dòng)autorelease
- personOne沒(méi)有注冊(cè)到pool中,因此超出作用域直接release;personTwo相反
objc_retainAutoreleasedReturnValue與objc_autoreleaseReturnValue
理解
- objc_retainAutoreleasedReturnValue會(huì)檢驗(yàn)調(diào)用者是否會(huì)對(duì)該對(duì)象執(zhí)行retain,如果會(huì)的話就不執(zhí)行autorelease,直接設(shè)置標(biāo)志符
- objc_autoreleaseReturnValue在檢驗(yàn)到標(biāo)志服后,也不retain了,直接返回對(duì)象本身
- 其實(shí)這種情況是最常見(jiàn)的了,方法的返回基本上是不會(huì)在autorelease了,這也是為什么上面提到對(duì)于方法的返回可以刪除autorelease部分,直接飲用計(jì)數(shù)+1就行
疑惑
- 這里我不太理解的是,按道理這樣子等于是有兩次retain操作,他就算優(yōu)化也應(yīng)該減少一次就行了,他怎么就把兩次都省了?
打印試驗(yàn)
#import "ViewController.h"
__weak id autoObj;
id stongObj;
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
id __autoreleasing obj = [NSMutableArray arrayWithObject:@"123"];
//NSLog(@"%@", obj);
autoObj = obj;
NSLog(@"1obj:%@", autoObj);
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
NSLog(@"2obj:%@", autoObj);
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
NSLog(@"3obj:%@", autoObj);
}
@end
- 結(jié)果為123 123 null
- 這里說(shuō)明確實(shí)注冊(cè)到了pool里,因?yàn)関iewDidAppear和上面兩個(gè)不在一個(gè)runloop里,導(dǎo)致最后打印不出來(lái)
- 我們把__autoreleasing換成strong,結(jié)果為123 null null,這里就是執(zhí)行上面說(shuō)的優(yōu)化操作,并沒(méi)有注冊(cè)到pool里
- 太神秘了
2019.7.26更新 至是疑始釋
- 引用一波明史里朱棣了解到建文帝下落后的描述,正好解釋下現(xiàn)在的心情
- 在和哲神交流一波后,真的是突然對(duì)很多東西豁然開(kāi)朗,貼下哲神的博客,接下來(lái)他也會(huì)更新runtime相關(guān)的文章咚個(gè)里個(gè)嗆
關(guān)于autorelease
- 其實(shí)整個(gè)引用計(jì)數(shù)管理都很清楚明白,retain,release,都簡(jiǎn)單不過(guò)了,只有autorelease比較抽象,難以理解
- 我們這里先想明白,在ARC中,我們不再需要顯式調(diào)用這幾個(gè)方法
- autorelease很多時(shí)候是為了那些不以alloc開(kāi)頭的方法,他們的要求是自己生成了但自己不持有,因此要使用autorelease,注冊(cè)到pool中
+ (NSString *)stringValue {
NSString *str = [NSString alloc] initWithFormat:@"I am this:%@", self];
return str;
}
NSString *str1 = [NSString stringValue];
- 在這種情況下,str1明顯不應(yīng)該持有返回的對(duì)象,而如果方法里直接就返回了,明顯會(huì)導(dǎo)致持有了
- 因此這個(gè)方法返回的實(shí)際上是[str autorelease]
- 這里我們可以這么理解,如果我們直接返回對(duì)象,和[str autorelease]都會(huì)使得引用計(jì)數(shù)+1了,區(qū)別在于
- NSString *str1 = str,將控制權(quán)給了str1,由他控制是否release
- NSString *str1 = [str autorelease],將控制權(quán)給了pool,由它決定什么時(shí)候release
- 且這個(gè)時(shí)候最開(kāi)始的str已經(jīng)超出了作用域,release了
- 這也就是為什么說(shuō)只比期望值多了一,期望值是initWithFormat方法里加上的,那個(gè)多一就是被pool引用了
疑點(diǎn)
- 目前的疑點(diǎn)還是在于小藍(lán)書(shū)的前后不一致,或者是理解還不到位
- 在p125下面提到autorelease與retain都是多余的,如果刪除了這兩步操作,顯然在執(zhí)行完賦值操作以后,引用計(jì)數(shù)不變,放這里就是和init時(shí)候一樣就對(duì)了,即與期望值一樣了
- 而后面又說(shuō),規(guī)定從方法中返回的對(duì)象其引用計(jì)數(shù)都比期望值多一就好
- 這個(gè)多的一要么是str1 retain了,要么是str autorelease的,如果都刪去,還怎么多這個(gè)1?
- p126的優(yōu)化同樣也是這個(gè)道理,如果兩個(gè)方法都沒(méi)走else,也就是和期望值一樣,這里前后矛盾讓還是無(wú)法好好理解
- 所以這里我還是覺(jué)得,引入持有這個(gè)概念就是雙刃劍,雖然前期提到了閱讀性,但后面直接導(dǎo)致一直要思考誰(shuí)持有誰(shuí),誰(shuí)被釋放了,不如一直研究對(duì)象的引用計(jì)數(shù)
解
- 其實(shí)理解到最后就是ARC時(shí)代,為了簡(jiǎn)化掉多余的操作,所有的運(yùn)行都是以期望值來(lái)決定
- 就以上面這個(gè)例子一開(kāi)始str持有對(duì)象,后來(lái)理應(yīng)pool持有對(duì)象,最后應(yīng)該是str1持有對(duì)象
- 但從我們的實(shí)際需求來(lái)說(shuō),我們只需要讓str1持有對(duì)象,因此只要return str,不進(jìn)行retain之類的操作,就OK了
- 從這個(gè)角度出發(fā),可以便于理解這些簡(jiǎn)化
