iOS ARC全解?

問題
簡(jiǎn)單介紹 ARC 以及 ARC 實(shí)現(xiàn)的原理幌甘。
考查點(diǎn)

我記得在剛接觸iOS的時(shí)候?qū)@個(gè)ARC和MRC就討論頗深硼控,認(rèn)為ARC是對(duì)程序員的一種福利,讓我們節(jié)省了大量的代碼请契,那么ARC是什么呢咳榜?

ARC 是蘋果在 WWDC 2011 提出來的技術(shù),因此很多新入行的同學(xué)可能對(duì)此技術(shù)細(xì)節(jié)并不熟悉姚糊。但是贿衍,雖然 ARC 極大地簡(jiǎn)化了我們的內(nèi)存管理工作,但是引用計(jì)數(shù)這種內(nèi)存管理方案如果不被理解救恨,那么就無法處理好那些棘手的循環(huán)引用問題贸辈。所以,這道面試題其實(shí)是考查同學(xué)對(duì)于 iOS 程序內(nèi)存管理的理解深度肠槽。
答案

自動(dòng)的引用計(jì)數(shù)(Automatic Reference Count 簡(jiǎn)稱 ARC)擎淤,是蘋果在 WWDC 2011 年大會(huì)上提出的用于內(nèi)存管理的技術(shù)。

引用計(jì)數(shù)(Reference Count)是一個(gè)簡(jiǎn)單而有效的管理對(duì)象生命周期的方式秸仙。當(dāng)我們創(chuàng)建一個(gè)新對(duì)象的時(shí)候嘴拢,它的引用計(jì)數(shù)為 1,當(dāng)有一個(gè)新的指針指向這個(gè)對(duì)象時(shí)寂纪,我們將其引用計(jì)數(shù)加 1席吴,當(dāng)某個(gè)指針不再指向這個(gè)對(duì)象是,我們將其引用計(jì)數(shù)減 1捞蛋,當(dāng)對(duì)象的引用計(jì)數(shù)變?yōu)?0 時(shí)孝冒,說明這個(gè)對(duì)象不再被任何指針指向了,這個(gè)時(shí)候我們就可以將對(duì)象銷毀拟杉,回收內(nèi)存庄涡。由于引用計(jì)數(shù)簡(jiǎn)單有效,除了 Objective-C 語言外搬设,微軟的 COM(Component Object Model )穴店、C++11(C++11 提供了基于引用計(jì)數(shù)的智能指針 share_prt) 等語言也提供了基于引用計(jì)數(shù)的內(nèi)存管理方式。

引用計(jì)數(shù)這種內(nèi)存管理方式雖然簡(jiǎn)單拿穴,但是手工寫大量的操作引用計(jì)數(shù)的代碼不但繁瑣泣洞,而且容易被遺漏。于是蘋果在 2011 年引入了 ARC默色。ARC 顧名思義斜棚,是自動(dòng)幫我們填寫引用計(jì)數(shù)代碼的一項(xiàng)功能。

ARC 的想法來源于蘋果在早期設(shè)計(jì) Xcode 的 Analyzer 的時(shí)候该窗,發(fā)現(xiàn)編譯器在編譯時(shí)可以幫助大家發(fā)現(xiàn)很多內(nèi)存管理中的問題弟蚀。后來蘋果就想,能不能干脆編譯器在編譯的時(shí)候酗失,把內(nèi)存管理的代碼都自動(dòng)補(bǔ)上义钉,帶著這種想法,蘋果修改了一些內(nèi)存管理代碼的書寫方式(例如引入了 @autoreleasepool 關(guān)鍵字)后规肴,在 Xcode 中實(shí)現(xiàn)了這個(gè)想法捶闸。

ARC 的工作原理大致是這樣:當(dāng)我們編譯源碼的時(shí)候,編譯器會(huì)分析源碼中每個(gè)對(duì)象的生命周期拖刃,然后基于這些對(duì)象的生命周期删壮,來添加相應(yīng)的引用計(jì)數(shù)操作代碼。所以兑牡,ARC 是工作在編譯期的一種技術(shù)方案央碟,這樣的好處是:

編譯之后,ARC 與非 ARC 代碼是沒有什么差別的均函,所以二者可以在源碼中共存亿虽。實(shí)際上,你可以通過編譯參數(shù) -fno-objc-arc 來關(guān)閉部分源代碼的 ARC 特性苞也。

相對(duì)于垃圾回收這類內(nèi)存管理方案洛勉,ARC 不會(huì)帶來運(yùn)行時(shí)的額外開銷,所以對(duì)于應(yīng)用的運(yùn)行效率不會(huì)有影響如迟。相反收毫,由于 ARC 能夠深度分析每一個(gè)對(duì)象的生命周期,它能夠做到比人工管理引用計(jì)數(shù)更加高效殷勘。例如在一個(gè)函數(shù)中此再,對(duì)一個(gè)對(duì)象剛開始有一個(gè)引用計(jì)數(shù) +1 的操作,之后又緊接著有一個(gè) -1 的操作劳吠,那么編譯器就可以把這兩個(gè)操作都優(yōu)化掉引润。

但是也有人認(rèn)為,ARC 也附帶有運(yùn)行期的一些機(jī)制來使 ARC 能夠更好的工作痒玩,他們主要是指 weak 關(guān)鍵字淳附。weak 變量能夠在引用計(jì)數(shù)為 0 時(shí)被自動(dòng)設(shè)置成 nil,顯然是有運(yùn)行時(shí)邏輯在工作的蠢古。我通常并沒有把這個(gè)算在 ARC 的概念當(dāng)中奴曙,當(dāng)然,這更多是一個(gè)概念或定義上的分歧草讶,因?yàn)槌_ weak 邏輯之外洽糟,ARC 核心的代碼都是在編譯期填充的。

作者:優(yōu)雅地小男子


高級(jí)解析


前言

本文的ARC特指Objective C的ARC,并不會(huì)講解其他語言坤溃。另外拍霜,本文涉及到的原理部分較多,適合有一定經(jīng)驗(yàn)的開發(fā)者薪介。

什么是ARC祠饺?

ARC的全稱Auto Reference Counting. 也就是自動(dòng)引用計(jì)數(shù)。那么汁政,為什么要有ARC呢道偷?

我們從C語言開始。使用C語言編程的時(shí)候记劈,如果要在堆上分配一塊內(nèi)存勺鸦,代碼如下

`//分配內(nèi)存(malloc/calloc均可)`

`int * array = calloc(10, sizeof (int));`

`//釋放內(nèi)存`

`free(array);1234512345`

C是面向過程的語言(Procedural programming),這種內(nèi)存的管理方式簡(jiǎn)單直接目木。但是换途,對(duì)于面向?qū)ο缶幊蹋@種手動(dòng)的分配釋放毫無疑問會(huì)大大的增加代碼的復(fù)雜度嘶窄。

于是怀跛,OOP的語言引入了各種各樣的內(nèi)存管理方法,比如Java的垃圾回收和Objective C的引用計(jì)數(shù)柄冲。關(guān)于垃圾回收和飲用計(jì)數(shù)的對(duì)比吻谋,可以參見Brad Larson的這個(gè)SO回答。

Objective C的引用計(jì)數(shù)理解起來很容易现横,當(dāng)一個(gè)對(duì)象被持有的時(shí)候計(jì)數(shù)加一漓拾,不再被持有的時(shí)候引用計(jì)數(shù)減一,當(dāng)引用計(jì)數(shù)為零的時(shí)候戒祠,說明這個(gè)對(duì)象已經(jīng)無用了骇两,則將其釋放。

引用計(jì)數(shù)分為兩種:

  • 手動(dòng)引用計(jì)數(shù)(MRC)

  • 自動(dòng)引用計(jì)數(shù)(ARC)

iOS開發(fā)早期姜盈,編寫代碼是采用MRC的


`// MRC代碼`

`NSObject * obj = [[NSObject alloc] init]; ``//引用計(jì)數(shù)為1`

`//不需要的時(shí)候`

`[obj release] ``//引用計(jì)數(shù)減1`

`//持有這個(gè)對(duì)象`

`[obj retain] ``//引用計(jì)數(shù)加1`

`//放到AutoReleasePool`

`[obj autorelease]``//在auto release pool釋放的時(shí)候低千,引用計(jì)數(shù)減1`


雖說這種方式提供了面向?qū)ο蟮膬?nèi)存管理接口,但是開發(fā)者不得不花大量的時(shí)間在內(nèi)存管理上馏颂,并且容易出現(xiàn)內(nèi)存泄漏或者release一個(gè)已被釋放的對(duì)象示血,導(dǎo)致crash。

再后來救拉,Apple對(duì)iOS/Mac OS開發(fā)引入了ARC难审。使用ARC,開發(fā)者不再需要手動(dòng)的retain/release/autorelease. 編譯器會(huì)自動(dòng)插入對(duì)應(yīng)的代碼亿絮,再結(jié)合Objective C的runtime告喊,實(shí)現(xiàn)自動(dòng)引用計(jì)數(shù)麸拄。

比如如下ARC代碼:


`NSObject * obj;`

`{`

`obj = [[NSObject alloc] init]; ``//引用計(jì)數(shù)為1`

`}`

`NSLog(@``"%@"``,obj);`


等同于如下MRC代碼

`NSObject * obj;`

`{`

`obj = [[NSObject alloc] init]; ``//引用計(jì)數(shù)為1`

`[obj relrease]`

`}`

`NSLog(@``"%@"``,obj);`

在Objective C中,有三種類型是ARC適用的:

  • block

  • objective 對(duì)象黔姜,id, Class, NSError*等

  • attribute((NSObject))標(biāo)記的類型拢切。

像double *,CFStringRef等不是ARC適用的,仍然需要手動(dòng)管理內(nèi)存地淀。

Tips: 以CF開頭的(Core Foundation)的對(duì)象往往需要手動(dòng)管理內(nèi)存失球。

屬性所有權(quán)

最后,我們?cè)诳纯碅RC中常見的所有權(quán)關(guān)鍵字帮毁,

  • assign對(duì)應(yīng)關(guān)鍵字__unsafe_unretained, 顧名思義,就是指向的對(duì)象被釋放的時(shí)候豺撑,仍然指向之前的地址烈疚,容易引起野指針。

  • copy對(duì)應(yīng)關(guān)鍵字__strong,只不過在賦值的時(shí)候聪轿,調(diào)用copy方法爷肝。

  • retain對(duì)應(yīng)__strong

  • strong對(duì)應(yīng)__strong

  • unsafe_unretained對(duì)應(yīng)__unsafe_unretained

  • weak對(duì)應(yīng)__weak。

其中陆错,__weak和__strong是本文要講解的核心內(nèi)容灯抛。

ARC的內(nèi)部實(shí)現(xiàn)

ARC背后的引用計(jì)數(shù)主要依賴于這三個(gè)方法:

  • retain 增加引用計(jì)數(shù)

  • release 降低引用計(jì)數(shù),引用計(jì)數(shù)為0的時(shí)候音瓷,釋放對(duì)象对嚼。

  • autorelease 在當(dāng)前的auto release pool結(jié)束后,降低引用計(jì)數(shù)绳慎。

在Cocoa Touch中纵竖,NSObject協(xié)議中定義了這三個(gè)方法,由于Cocoa Touch中杏愤,絕大部分類都繼承自NSObject(NSObject類本身實(shí)現(xiàn)了NSObject協(xié)議)靡砌,所以可以“免費(fèi)”獲得NSObject提供的運(yùn)行時(shí)和ARC管理方法,這就是為什么適用OC開發(fā)iOS的時(shí)候珊楼,你的類要繼承自NSObject通殃。

既然ARC是引用計(jì)數(shù),那么對(duì)應(yīng)一個(gè)對(duì)象厕宗,內(nèi)存中必然會(huì)有一個(gè)地方來存儲(chǔ)這個(gè)對(duì)象的引用計(jì)數(shù)画舌。iOS的Runtime是開源的,在這里可以下載到全部的代碼媳瞪,我們通過源代碼一探究竟骗炉。

我們從retain入手,


`- (id)retain {`

`return` `((id)self)->rootRetain();`

`}`

`inline id objc_object::rootRetain()`

`{`

`if` `(isTaggedPointer()) ``return` `(id)``this``;`

`return` `sidetable_retain();`

`}`


所以說,本質(zhì)上retain就是調(diào)用sidetable_retain蛇受,再看看sitetable_retain的實(shí)現(xiàn):


`id objc_object::sidetable_retain()`

`{`

`//獲取table`

`SideTable& table = SideTables()[``this``];`

`//加鎖`

`table.lock();`

`//獲取引用計(jì)數(shù)`

`size_t& refcntStorage = table.refcnts[``this``];`

`if` `(! (refcntStorage & SIDE_TABLE_RC_PINNED)) {`

`//增加引用計(jì)數(shù)`

`refcntStorage += SIDE_TABLE_RC_ONE;`

`}`

`//解鎖`

`table.unlock();`

`return` `(id)``this``;`

`}`


到這里句葵,retain如何實(shí)現(xiàn)就很清楚了,通過SideTable這個(gè)數(shù)據(jù)結(jié)構(gòu)來存儲(chǔ)引用計(jì)數(shù)。我們看看這個(gè)數(shù)據(jù)結(jié)構(gòu)的實(shí)現(xiàn):

QQ截圖20170421165138.png

可以看到乍丈,這個(gè)數(shù)據(jù)結(jié)構(gòu)就是存儲(chǔ)了一個(gè)自旋鎖剂碴,一個(gè)引用計(jì)數(shù)map。這個(gè)引用計(jì)數(shù)的map以對(duì)象的地址作為key轻专,引用計(jì)數(shù)作為value忆矛。到這里,引用計(jì)數(shù)的底層實(shí)現(xiàn)我們就很清楚了请垛。

存在全局的map催训,這個(gè)map以地址作為key,引用計(jì)數(shù)的值作為value宗收。

再來看看release的實(shí)現(xiàn):

`SideTable& table = SideTables()[``this``];`

`bool do_dealloc = ``false``;`

`table.lock();`

`//找到對(duì)應(yīng)地址的`

`RefcountMap::iterator it = table.refcnts.find(``this``);`

`if` `(it == table.refcnts.end()) { ``//找不到的話漫拭,執(zhí)行dellloc`

`do_dealloc = ``true``;`

`table.refcnts[``this``] = SIDE_TABLE_DEALLOCATING;`

`} ``else` `if` `(it->second < SIDE_TABLE_DEALLOCATING) {``//引用計(jì)數(shù)小于閾值,dealloc`

`do_dealloc = ``true``;`

`it->second |= SIDE_TABLE_DEALLOCATING;`

`} ``else` `if` `(! (it->second & SIDE_TABLE_RC_PINNED)) {`

`//引用計(jì)數(shù)減去1`

`it->second -= SIDE_TABLE_RC_ONE;`

`}`

`table.unlock();`

`if` `(do_dealloc  &&  performDealloc) {`

`//執(zhí)行dealloc`

`((void(*)(objc_object *, SEL))objc_msgSend)(``this``, SEL_dealloc);`

`}`

`return` `do_dealloc;`


release的到這里也比較清楚了:查找map混稽,對(duì)引用計(jì)數(shù)減1采驻,如果引用計(jì)數(shù)小于閾值,則調(diào)用SEL_dealloc

Autorelease pool

上文提到了匈勋,autorelease方法的作用是把對(duì)象放到autorelease pool中礼旅,到pool drain的時(shí)候,會(huì)釋放池中的對(duì)象洽洁。舉個(gè)例子

`__weak NSObject * obj;`

`NSObject * temp = [[NSObject alloc] init];`

`obj = temp;`

`NSLog(@``"%@"``,obj); ``//非空`

 |

放到auto release pool中痘系,


`__weak NSObject * obj;`

`@autoreleasepool {`

`NSObject * temp = [[NSObject alloc] init];`

`obj = temp;`

`}`

`NSLog(@``"%@"``,obj); ``//null`


可以看到,放到自動(dòng)釋放池的對(duì)象是在超出自動(dòng)釋放池作用域后立即釋放的诡挂。事實(shí)上在iOS 程序啟動(dòng)之后碎浇,主線程會(huì)啟動(dòng)一個(gè)Runloop,這個(gè)Runloop在每一次循環(huán)是被自動(dòng)釋放池包裹的璃俗,在合適的時(shí)候?qū)Τ刈舆M(jìn)行清空奴璃。

對(duì)于Cocoa框架來說,提供了兩種方式來把對(duì)象顯式的放入AutoReleasePool.

  • NSAutoreleasePool(只能在MRC下使用)

  • @autoreleasepool {}代碼塊(ARC和MRC下均可以使用)

那么AutoRelease pool又是如何實(shí)現(xiàn)的呢城豁?

我們先從autorelease方法源碼入手

`//autorelease方法`

`- (id)autorelease {`

`return` `((id)self)->rootAutorelease();`

`}`

`//rootAutorelease 方法`

`inline id objc_object::rootAutorelease()`

`{`

`if` `(isTaggedPointer()) ``return` `(id)``this``;`

`//檢查是否可以優(yōu)化`

`if` `(prepareOptimizedReturn(ReturnAtPlus1)) ``return` `(id)``this``;`

`//放到auto release pool中苟穆。`

`return` `rootAutorelease2();`

`}`

`// rootAutorelease2`

`id objc_object::rootAutorelease2()`

`{`

`assert(!isTaggedPointer());`

`return` `AutoreleasePoolPage::autorelease((id)``this``);`

`}`


可以看到,把一個(gè)對(duì)象放到auto release pool中唱星,是調(diào)用了AutoreleasePoolPage::autorelease這個(gè)方法雳旅。

我們繼續(xù)查看對(duì)應(yīng)的實(shí)現(xiàn):

`public: static inline id autorelease(id obj)`

`{`

`assert(obj);`

`assert(!obj->isTaggedPointer());`

`id *dest __unused = autoreleaseFast(obj);`

`assert(!dest  ||  dest == EMPTY_POOL_PLACEHOLDER  ||  *dest == obj);`

`return` `obj;`

`}`

`static inline id *autoreleaseFast(id obj)`

`{`

`AutoreleasePoolPage *page = hotPage();`

`if` `(page && !page->full()) {`

`return` `page->add(obj);`

`} ``else` `if` `(page) {`

`return` `autoreleaseFullPage(obj, page);`

`} ``else` `{`

`return` `autoreleaseNoPage(obj);`

`}`

`}`

`id *add(id obj)`

`{`

`assert(!full());`

`unprotect();`

`id *ret = next;  ``// faster than `return next-1` because of aliasing`

`*next++ = obj;`

`protect();`

`return` `ret;`

`}`


到這里,autorelease方法的實(shí)現(xiàn)就比較清楚了间聊,

autorelease方法會(huì)把對(duì)象存儲(chǔ)到AutoreleasePoolPage的鏈表里攒盈。等到auto release pool被釋放的時(shí)候,把鏈表內(nèi)存儲(chǔ)的對(duì)象刪除哎榴。所以型豁,AutoreleasePoolPage就是自動(dòng)釋放池的內(nèi)部實(shí)現(xiàn)僵蛛。

__weak與__strong

用過block的同學(xué)一定寫過類似的代碼:


`__weak typeSelf(self) weakSelf = self;`

`[object fetchSomeFromRemote:^{`

`__strong typeSelf(weakSelf) strongSelf = weakSelf;`

`//從這里開始用strongSelf`

`}];`


那么,為什么要這么用呢迎变?原因是:

block會(huì)捕獲外部變量充尉,用weakSelf保證self不會(huì)被block被捕獲,防止引起循環(huán)引用或者不必要的額外生命周期衣形。

用strongSelf則保證在block的執(zhí)行過程中驼侠,對(duì)象不會(huì)被釋放掉。

首先__strong和__weak都是關(guān)鍵字谆吴,是給編譯器理解的倒源。為了理解其原理,我們需要查看它們編譯后的代碼纪铺,使用XCode相速,我們可以容易的獲得一個(gè)文件的匯編代碼。

比如鲜锚,對(duì)于Test.m文件,當(dāng)源代碼如下時(shí):

`#import "Test.h"`

`@implementation Test`

`- (void)testFunction{`

`{`

`__strong NSObject * temp = [[NSObject alloc] init];`

`}`

`}`

`@end`


轉(zhuǎn)換后的匯編代碼如下:

`Ltmp3:`

`.loc    2 15 37 prologue_end    ; /Users/hl/Desktop/OCTest/OCTest/Test.m:15:37`

`ldr     x9, [x9]`

`ldr     x1, [x8]`

`mov  x0, x9`

`bl  _objc_msgSend`

`adrp    x8, L_OBJC_SELECTOR_REFERENCES_.2@PAGE`

`add x8, x8, L_OBJC_SELECTOR_REFERENCES_.2@PAGEOFF`

`.loc    2 15 36 is_stmt 0       ; /Users/hl/Desktop/OCTest/OCTest/Test.m:15:36`

`ldr     x1, [x8]`

`.loc    2 15 36 discriminator 1 ; /Users/hl/Desktop/OCTest/OCTest/Test.m:15:36`

`bl  _objc_msgSend`

`mov x8, ``#0`

`add x9, sp, ``#8              ; =8`

`.loc    2 15 29                 ; /Users/hl/Desktop/OCTest/OCTest/Test.m:15:29`

`str x0, [sp, ``#8]`

`Ltmp4:`

`.loc    2 16 5 is_stmt 1        ; /Users/hl/Desktop/OCTest/OCTest/Test.m:16:5`

`mov  x0, x9`

`mov  x1, x8`

`bl  _objc_storeStrong`

`.loc    2 17 1                  ; /Users/hl/Desktop/OCTest/OCTest/Test.m:17:1`

`ldp x29, x30, [sp, ``#32]     ; 8-byte Folded Reload`

`add sp, sp, ``#48             ; =48`

`ret`

`Ltmp5:`


即使你不懂匯編苫拍,也能很輕易的獲取到調(diào)用順序如下


`_objc_msgSend ``// alloc`

`_objc_msgSend ``// init`

`_objc_storeStrong ``// 強(qiáng)引用`


在結(jié)合Runtime的源碼芜繁,我們看看最關(guān)鍵的objc_storeStrong的實(shí)現(xiàn)

`void objc_storeStrong(id *location, id obj)`

`{`

`id prev = *location;`

`if` `(obj == prev) {`

`return``;`

`}`

`objc_retain(obj);`

`*location = obj;`

`objc_release(prev);`

`}`

`id objc_retain(id obj) { ``return` `[obj retain]; }`

`void objc_release(id obj) { [obj release]; }`


我們?cè)賮砜纯確_weak. 將Test.m修改成為如下代碼,同樣我們分析其匯編實(shí)現(xiàn)

`.loc    2 15 35 prologue_end    ; /Users/hl/Desktop/OCTest/OCTest/Test.m:15:35`

`ldr     x9, [x9]`

`ldr     x1, [x8]`

`mov  x0, x9`

`bl  _objc_msgSend`

`adrp    x8, L_OBJC_SELECTOR_REFERENCES_.2@PAGE`

`add x8, x8, L_OBJC_SELECTOR_REFERENCES_.2@PAGEOFF`

`.loc    2 15 34 is_stmt 0       ; /Users/hl/Desktop/OCTest/OCTest/Test.m:15:34`

`ldr     x1, [x8]`

`.loc    2 15 34 discriminator 1 ; /Users/hl/Desktop/OCTest/OCTest/Test.m:15:34`

`bl  _objc_msgSend`

`add x8, sp, ``#24             ; =24`

`.loc    2 15 27                 ; /Users/hl/Desktop/OCTest/OCTest/Test.m:15:27`

`mov  x1, x0`

`.loc    2 15 27 discriminator 2 ; /Users/hl/Desktop/OCTest/OCTest/Test.m:15:27`

`str x0, [sp, ``#16]           ; 8-byte Folded Spill`

`mov  x0, x8`

`bl  _objc_initWeak`

`.loc    2 15 27                 ; /Users/hl/Desktop/OCTest/OCTest/Test.m:15:27`

`ldr x1, [sp, ``#16]           ; 8-byte Folded Reload`

`.loc    2 15 27 discriminator 3 ; /Users/hl/Desktop/OCTest/OCTest/Test.m:15:27`

`str x0, [sp, ``#8]            ; 8-byte Folded Spill`

`mov  x0, x1`

`bl  _objc_release`

`add x8, sp, ``#24  `

`Ltmp4:`

`.loc    2 16 5 is_stmt 1        ; /Users/hl/Desktop/OCTest/OCTest/Test.m:16:5`

`mov  x0, x8`

`bl  _objc_destroyWeak`

`.loc    2 17 1                  ; /Users/hl/Desktop/OCTest/OCTest/Test.m:17:1`

`ldp x29, x30, [sp, ``#48]     ; 8-byte Folded Reload`

`add sp, sp, ``#64             ; =64`

`ret`


可以看到绒极,__weak本身實(shí)現(xiàn)的核心就是以下兩個(gè)方法

  • _objc_initWeak

  • _objc_destroyWeak

我們通過Runtime的源碼分析這兩個(gè)方法的實(shí)現(xiàn):

<false></false>


`id objc_initWeak(id *location, id newObj)`

`{`

`//省略....`

`return` `storeWeak        (location, (objc_object*)newObj);`
`}`
`void objc_destroyWeak(id *location)`
`{`
`(void)storeWeak        (location, nil);`
`}`


所以骏令,本質(zhì)上都是調(diào)用了storeWeak函數(shù),這個(gè)函數(shù)內(nèi)容較多垄提,主要做了以下事情

  • 獲取存儲(chǔ)weak對(duì)象的map榔袋,這個(gè)map的key是對(duì)象的地址,value是weak引用的地址铡俐。

  • 當(dāng)對(duì)象被釋放的時(shí)候凰兑,根據(jù)對(duì)象的地址可以找到對(duì)應(yīng)的weak引用的地址,將其置為nil即可审丘。

這就是在weak背后的黑魔法吏够。

總結(jié)

這篇文章屬于想到哪里寫到哪里的類型,后邊有時(shí)間了在繼續(xù)總結(jié)ARC的東西吧滩报。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末锅知,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子脓钾,更是在濱河造成了極大的恐慌售睹,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,682評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件可训,死亡現(xiàn)場(chǎng)離奇詭異昌妹,居然都是意外死亡捶枢,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門捺宗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來柱蟀,“玉大人,你說我怎么就攤上這事蚜厉〕ひ眩” “怎么了?”我有些...
    開封第一講書人閱讀 165,083評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵昼牛,是天一觀的道長(zhǎng)术瓮。 經(jīng)常有香客問我,道長(zhǎng)贰健,這世上最難降的妖魔是什么胞四? 我笑而不...
    開封第一講書人閱讀 58,763評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮伶椿,結(jié)果婚禮上辜伟,老公的妹妹穿的比我還像新娘。我一直安慰自己脊另,他們只是感情好导狡,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,785評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著偎痛,像睡著了一般旱捧。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上踩麦,一...
    開封第一講書人閱讀 51,624評(píng)論 1 305
  • 那天枚赡,我揣著相機(jī)與錄音,去河邊找鬼谓谦。 笑死贫橙,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的茁计。 我是一名探鬼主播料皇,決...
    沈念sama閱讀 40,358評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼星压!你這毒婦竟也來了践剂?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,261評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤娜膘,失蹤者是張志新(化名)和其女友劉穎逊脯,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體竣贪,經(jīng)...
    沈念sama閱讀 45,722評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡军洼,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年巩螃,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片匕争。...
    茶點(diǎn)故事閱讀 40,030評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡避乏,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出甘桑,到底是詐尸還是另有隱情拍皮,我是刑警寧澤,帶...
    沈念sama閱讀 35,737評(píng)論 5 346
  • 正文 年R本政府宣布跑杭,位于F島的核電站铆帽,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏德谅。R本人自食惡果不足惜爹橱,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,360評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望窄做。 院中可真熱鬧愧驱,春花似錦、人聲如沸椭盏。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽庸汗。三九已至,卻和暖如春手报,著一層夾襖步出監(jiān)牢的瞬間蚯舱,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評(píng)論 1 270
  • 我被黑心中介騙來泰國(guó)打工掩蛤, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留枉昏,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,237評(píng)論 3 371
  • 正文 我出身青樓揍鸟,卻偏偏與公主長(zhǎng)得像兄裂,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子阳藻,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,976評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹(jǐn) 對(duì)...
    cosWriter閱讀 11,103評(píng)論 1 32
  • 內(nèi)存管理 簡(jiǎn)述OC中內(nèi)存管理機(jī)制晰奖。與retain配對(duì)使用的方法是dealloc還是release,為什么腥泥?需要與a...
    丶逐漸閱讀 1,964評(píng)論 1 16
  • 29.理解引用計(jì)數(shù) Objective-C語言使用引用計(jì)數(shù)來管理內(nèi)存匾南,也就是說,每個(gè)對(duì)象都有個(gè)可以遞增或遞減的計(jì)數(shù)...
    Code_Ninja閱讀 1,490評(píng)論 1 3
  • 概述 在iOS中開發(fā)中蛔外,我們或多或少都聽說過內(nèi)存管理蛆楞。iOS的內(nèi)存管理一般指的是OC對(duì)象的內(nèi)存管理溯乒,因?yàn)镺C對(duì)象分...
    DamonMok閱讀 3,999評(píng)論 2 20
  • 焦點(diǎn)網(wǎng)絡(luò)中級(jí)八期 洛陽 杜紅平 堅(jiān)持分享第 160 天 昨天中午,我們學(xué)校一行九人和洛龍區(qū)的老師們一起坐大巴...
    隨喜Prajana閱讀 274評(píng)論 0 0