《iOS高級(jí)編程》中的ARC

關(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)存管理四大原則

  1. 自己生成的對(duì)象,自己持有
  2. 非自己生成的對(duì)象,自己也能持有
  3. 不再需要自己持有的對(duì)象時(shí)釋放
  4. 非自己持有的對(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)鏈:
    1. obj超出作用域,obj失效,不再持有該對(duì)象
    2. 失去強(qiáng)引用,對(duì)象不在被持有【也就是引用計(jì)數(shù)從1到0,自動(dòng)dealloc】
    3. 對(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的使用方法
    1. 生成并持有NSAutoreleasePool對(duì)象。
    2. 調(diào)用已分配對(duì)象的autorelease方法。【將對(duì)象注冊(cè)到pool中】
    3. 廢棄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的例子

  1. 這個(gè)方法以"new"開(kāi)頭,既然"person"已經(jīng)使得引用計(jì)數(shù)+1了【因?yàn)閍lloc】,那么我們不需要做任何操作了,直接返回就行;也就是說(shuō),以alloc開(kāi)頭的方法,只要保證返回的對(duì)象引用計(jì)數(shù)+1了就行
  2. 該方法不是以那幾個(gè)開(kāi)頭,因此返回時(shí)自動(dòng)autorelease
  3. 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ū)別在于
    1. NSString *str1 = str,將控制權(quán)給了str1,由他控制是否release
    2. 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)化
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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