Objective-C高級(jí)編程:iOS與OS X多線程和內(nèi)存管理(一)

1 什么是自動(dòng)引用計(jì)數(shù)

自動(dòng)引用計(jì)數(shù)是指內(nèi)存管理中對(duì)引用采取自動(dòng)計(jì)數(shù)的技術(shù)拴鸵。在 Objective-C 中采用 ARC 機(jī)制咒彤,讓編譯器來(lái)進(jìn)行內(nèi)存管理苗分。

2 內(nèi)存管理 / 引用計(jì)數(shù)

用開(kāi)關(guān)房間的燈為例來(lái)說(shuō)明引用計(jì)數(shù)的機(jī)制

2.1 內(nèi)存管理的思考方式

自己生成的對(duì)象正驻,自己所持有弊攘。

非自己生成的對(duì)象抢腐,自己也能持有。

不再需要自己持有的對(duì)象時(shí)釋放襟交。

非自己持有的對(duì)象無(wú)法釋放

自己生成的對(duì)象自己持有:alloc? new? ?copy? mutableCopy? 以及以這些名稱開(kāi)頭的方法名( allocMyObject? newThatObject? copyThis…)? ;? 不屬于這類的方法名(allocate? newer? coping…)

非自己生成的對(duì)象迈倍,自己也能持有retain 方法

例: id obj = [NSMutableArray? array]; //取得對(duì)象存在,但自己不持有對(duì)象

[obj? retain]; //自己持有對(duì)象

不再需要自己持有的對(duì)象時(shí)釋放:release 方法

用 alloc/new/copy/mutableCopy 方法生成并持有的對(duì)象捣域,或者用 retain 方法持有的對(duì)象啼染,一旦不再需要,務(wù)必要用 release 方法進(jìn)行釋放竟宋。

release與autorelease? 的區(qū)別:release 立即釋放; autorelease 放入釋放池中提完,釋放池結(jié)束時(shí)釋放。

無(wú)法釋放非自己持有的對(duì)象:釋放非自己所持有的對(duì)象時(shí)會(huì)造成崩潰丘侠。

2.2 alloc/retain/release/dealloc 蘋(píng)果的實(shí)現(xiàn)? (使用引用計(jì)數(shù)表)

引用計(jì)數(shù)表各記錄中存有內(nèi)存塊地址徒欣,可從各個(gè)記錄追溯到各對(duì)象的內(nèi)存塊。

利用工具檢測(cè)內(nèi)存泄漏時(shí)蜗字,引用計(jì)數(shù)表的各記錄有助于檢測(cè)各對(duì)象的持有者是否存在

2.3 autorelease

autorelease:當(dāng)超出其作用域時(shí)打肝,對(duì)象實(shí)例的 release 實(shí)例方法被調(diào)用。

autorelease具體使用方法:

生成并持有 NSAutoreleasePool 對(duì)象挪捕。

調(diào)用已分配對(duì)象的 autorelease 實(shí)例方法粗梭。// [obj? autorelease]

廢棄 NSAutoreleasePool 對(duì)象。 // [pool drain]?


Cocoa 框架中有很多類方法用于返回 autorelease 的對(duì)象级零。比如 NSMutableArray 類的 arrayWithCapacity 類方法断医。

2.4 autorelease 蘋(píng)果的實(shí)現(xiàn)

NSObject 類的 autorelese 實(shí)例方法,將該對(duì)象追加到正在使用的 NSAutoreleasePool 對(duì)象的數(shù)組中奏纪。

查看已被 autorelease 的對(duì)象的狀態(tài):兩種

[NSAutoreleasePool? showPools]; //只能在 iOS 中使用

_objc_autoreleasePoolPrint( ); //在運(yùn)行時(shí)系統(tǒng)中調(diào)用

如果 autorelease NSAutoreleasePool 對(duì)象會(huì)如何鉴嗤?

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

[pool autorelease];

答:會(huì)報(bào) Cannot autorelease an autorelease pool

調(diào)用對(duì)象的 autorelease 實(shí)例方法,實(shí)際上是調(diào)用 NSObject 類的 autorelease 實(shí)例方法序调。但是對(duì)于 NSAutoreleasePool 類醉锅,autorelease 實(shí)例方法已被該類重載,因此運(yùn)行時(shí)會(huì)出錯(cuò)发绢。

3 ARC 規(guī)則

3.1 概要

文件的編譯屬性設(shè)置:-fobjc-arc 或 -fno-objc-arc

3.2 內(nèi)存管理的思考方式

引用計(jì)數(shù)式內(nèi)存管理的思考方式就是思考 ARC 所引起的變化硬耍。

3.3 所有權(quán)修飾符

__strong 修飾符?

是 id 類型和對(duì)象類型默認(rèn)的所有權(quán)修飾符。

__strong边酒、__weak经柴、__autoreleasing 修飾,可以保證有這些修飾符的自動(dòng)變量初始化為nil墩朦。

通過(guò) __strong 修飾符口锭,不必再次鍵入 retain 或者 release,完美地滿足了“引用計(jì)數(shù)式內(nèi)存管理的思考方式”介杆。

__weak 修飾符

主要用于解決“循環(huán)引用”的問(wèn)題鹃操,用 __weak 修飾符可以避免循環(huán)引用。

id __weak obj = [[NSObject alloc] init];? //生成的對(duì)象會(huì)立即被釋放春哨。

__unsafe_unretained 修飾符

是不安全的所有權(quán)修飾符荆隘,__unsafe_unretained 修飾符的變量不屬于編譯器的內(nèi)存管理對(duì)象

賦值給附有 __unsafe_unretained 修飾符變量的對(duì)象在通過(guò)該變量使用時(shí),如果沒(méi)有確定其確實(shí)存在赴背,那么應(yīng)用程序就會(huì)崩潰椰拒。

為什么需要使用附有 __unsafe_unretained 修飾符的變量?

比如在 iOS4 以及 OS X Snow Leopard 的應(yīng)用程序中凰荚,必須使用 __unsafe_unretained 修飾符來(lái)替代 __weak 修飾符燃观。

__autoreleasing 修飾符

在 ARC 有效時(shí),用 @autoreleasepool 塊替代 NSAutoreleasePool 類便瑟,用附有 __autoreleasing 修飾符的變量替代 autorelease 方法缆毁。

不是以 alloc/new/copy/mutableCopy 開(kāi)頭的方法(init 系列方法除外) 返回的對(duì)象將自動(dòng)注冊(cè)到 autoreleasepool 。

在訪問(wèn)附有 __weak 修飾符的變量時(shí)到涂,實(shí)際上必定要訪問(wèn)注冊(cè)到 autoreleasepool 的對(duì)象脊框。?

原因:因?yàn)?__weak 修飾符只持有對(duì)象的弱引用,而在訪問(wèn)引用對(duì)象的過(guò)程中践啄,該對(duì)象有可能被廢棄浇雹。如果把要訪問(wèn)的對(duì)象注冊(cè)到 autoreleasepool 中,那么在 @autoreleasepool 塊結(jié)束之前都能確保該對(duì)象存在屿讽。因此昭灵,在使用附有 __weak 修飾符的變量時(shí)就必定要使用注冊(cè)到 autorelease 中的對(duì)象。?

id 的指針或?qū)ο蟮闹羔樤跊](méi)有顯式指定時(shí)會(huì)被附加上 __autorelease 修飾符伐谈。

賦值給對(duì)象指針時(shí)烂完,所有權(quán)修飾符必須一致。

NSError *error = nil;

NSError *__strong *pError = &error;

NSError __weak *error = nil;

NSError * __weak *pError = &error;

例:

方法聲明:

(BOOL) performOperationWithError: (NSError * __autoreleasing *)error;?

方法調(diào)用:

NSError __strong *error = nil;

BOOL return = [obj performOperationWithError: &error];

編譯器轉(zhuǎn)換為:

NSError __strong *error = nil;

NSError __autoreleasing *tmp = error;

BOOL result = [obj performOperationWithError: &tmp];

error = tmp;

方法聲明也可以寫(xiě)為:

(BOOL) performOperationWithError: (NSError * __strong *)error;?

即對(duì)象不注冊(cè)到 autoreleasepool 也能夠傳遞衩婚,但是為了貫徹內(nèi)存管理的思考方式窜护,我們要將參數(shù)聲明為附有 __autoreleasing 修飾符的對(duì)象指針類型。

3.4 規(guī)則

不能使用 retain/release/retainCount/autorelease

不能使用 NSAllocateObject/NSDeallocateObject

須遵守內(nèi)存管理的方法命名規(guī)則

用于對(duì)象生成/持有的方法必須遵守以下命名規(guī)則:以 alloc/new/copy/mutableCopy 名稱開(kāi)始的方法在返回對(duì)象時(shí)非春,必須返回給調(diào)用方所應(yīng)當(dāng)持有的對(duì)象柱徙。(alloc 是以 NSAllocateObject 分配對(duì)象)。

以 init 開(kāi)始的方法返回的對(duì)象不注冊(cè)到 autoreleasepool 上奇昙, 基本上只是對(duì) alloc 方法返回值的對(duì)象進(jìn)行初始化處理并返回該對(duì)象护侮。?

不要顯式調(diào)用 dealloc

使用 @autoreleasepool 塊替代 NSAutoreleasePool

不能使用區(qū)域 (NSZone)

不管 ARC 是否有效,區(qū)域在現(xiàn)在的運(yùn)行時(shí)系統(tǒng)中已單純地被忽略储耐。

對(duì)象型變量不能作為 C 語(yǔ)言結(jié)構(gòu)體 (struct/union) 的成員

原因:C 語(yǔ)言的規(guī)約上沒(méi)有方法來(lái)管理結(jié)構(gòu)體成員的生存周期羊初。因?yàn)?ARC 把內(nèi)存管理的工作分配給編譯器,所以編譯器必須能夠知道并管理對(duì)象的生存周期。

顯式轉(zhuǎn)換 “id” ”void * “

通過(guò) “__bridge 轉(zhuǎn)換”长赞, id 和 void * 能夠相互轉(zhuǎn)換(__bridge轉(zhuǎn)換安全性低)

__bridge還有另外兩種轉(zhuǎn)換:__bridge_retained轉(zhuǎn)換? 和? __bridge_transfer轉(zhuǎn)換晦攒。

__bridge_retained轉(zhuǎn)換可使要轉(zhuǎn)換賦值的變量也持有所賦值的對(duì)象。

代碼:

id obj = [[NSObject alloc] init];

void *p = (__bridge_retained void *) obj;

相當(dāng)于:ARC 無(wú)效時(shí)的源代碼

id obj = [[NSObject alloc] init];

void *p = obj;?

[(id)p retain];

__bridge_transfer轉(zhuǎn)換 被轉(zhuǎn)換的變量所持有的對(duì)象在該變量被賦值給轉(zhuǎn)換目標(biāo)變量后隨之釋放得哆。

代碼:

id obj = (__bridge_transfer id) obj;

相當(dāng)于:ARC 無(wú)效時(shí)的源代碼

id obj = (id) p;

[obj retain];

[(id)p release];

主要應(yīng)用于 Objective-C 對(duì)象與 Core Foundation 對(duì)象之間轉(zhuǎn)換脯颜。

3.5 屬性?

3.6 數(shù)組?

使用 calloc 函數(shù)確保想分配的附有 __strong 修飾符變量占有的內(nèi)存塊。

array = (id __strong *)calloc (entries,sizeof(id));

該源代碼分配了 entries 個(gè)所需的內(nèi)存塊贩据。由于使用附有 __strong 修飾符的變量前必須先將其初始化為 nil, 所以這里使用使分配區(qū)域初始化為0的 calloc 函數(shù)來(lái)分配內(nèi)存栋操。不使用 malloc 函數(shù),在用 malloc 函數(shù)分配內(nèi)存后可用 memset 等函數(shù)將內(nèi)存填充為0饱亮。

free(array)矾芙;

只是簡(jiǎn)單地用 free 函數(shù)廢棄了數(shù)組用內(nèi)存塊的情況下,數(shù)組各元素所賦值的對(duì)象不能再次釋放近上,從而引起內(nèi)存泄漏剔宪。

這是因?yàn)樵诒響B(tài)數(shù)組中,編譯器能夠根據(jù)變量的作用域自動(dòng)插入釋放賦值對(duì)象的代碼戈锻,而在動(dòng)態(tài)數(shù)組中歼跟,編譯器不能確定數(shù)組的生存周期,所以無(wú)從處理格遭。一定要將 nil 賦值給所有元素哈街,使得元素所賦值對(duì)象的強(qiáng)引用失效,從而釋放那些對(duì)象拒迅。在此之后骚秦,使用 free 函數(shù)廢棄內(nèi)存塊。

4 ARC 的實(shí)現(xiàn)

4.1 __strong 修飾符

objc_retainAutoreleasedReturnValue函數(shù)與 objc_autoreleaseReturnValue 函數(shù)

如果調(diào)用了 objc_autoreleaseReturnValue() 后緊接著調(diào)用 objc_retainAutoreleasedReturnValue() 璧微, 那么就不將返回的對(duì)象注冊(cè)到 autoreleasepool 中作箍,而是直接傳遞到調(diào)用方。達(dá)到最優(yōu)化前硫。?

4.2 __weak 修飾符

附有 __weak 修飾符的變量所引用的對(duì)象被廢棄胞得,則將 nil 賦值給該變量。

使用附有 __weak 修飾符的變量屹电,即是使用注冊(cè)到 autoreleasepool 中的對(duì)象阶剑。

對(duì)象被廢棄時(shí)最后調(diào)用的 objc_clear_deallocating 函數(shù)的動(dòng)作如下:

只在需要避免循環(huán)引用時(shí)使用__weak 修飾符

因?yàn)槭褂酶接?__weak 修飾符變量,會(huì)注冊(cè)到 autoreleasepool 的對(duì)象中危号。大量使用就會(huì)大量注冊(cè)牧愁,因此使用附有 __weak 修飾符的變量時(shí),最好先暫時(shí)賦值給附有 __strong 修飾符的變量后再使用外莲。

4.3 __autoreleasing 修飾符

將對(duì)象賦值給附有 __autoreleasing 修飾符的變量等同于 ARC 無(wú)效時(shí)調(diào)用對(duì)象的 autorelease 方法猪半。

4.4 引用計(jì)數(shù)

_objc_rootRetainCount函數(shù)可獲取指定對(duì)象的引用計(jì)數(shù)數(shù)值。

引用計(jì)數(shù)數(shù)值變化:

賦值給附有 __weak 修飾符的變量,引用計(jì)數(shù)不變磨确。

被注冊(cè)到 autoreleasepool 中的對(duì)象沽甥,引用計(jì)數(shù)會(huì) +1。

使用附有 __weak 修飾符的變量俐填,會(huì)將對(duì)象注冊(cè)到 autoreleasepool 中安接,引用計(jì)數(shù)會(huì) +1。



注:代碼使 __weak 生效

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末英融,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子歇式,更是在濱河造成了極大的恐慌驶悟,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,817評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件材失,死亡現(xiàn)場(chǎng)離奇詭異痕鳍,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)龙巨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,329評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén)笼呆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人旨别,你說(shuō)我怎么就攤上這事诗赌。” “怎么了秸弛?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,354評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵铭若,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我递览,道長(zhǎng)叼屠,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,498評(píng)論 1 284
  • 正文 為了忘掉前任绞铃,我火速辦了婚禮镜雨,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘儿捧。我一直安慰自己荚坞,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,600評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布纯命。 她就那樣靜靜地躺著西剥,像睡著了一般。 火紅的嫁衣襯著肌膚如雪亿汞。 梳的紋絲不亂的頭發(fā)上瞭空,一...
    開(kāi)封第一講書(shū)人閱讀 49,829評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼咆畏。 笑死南捂,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的旧找。 我是一名探鬼主播溺健,決...
    沈念sama閱讀 38,979評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼钮蛛!你這毒婦竟也來(lái)了鞭缭?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,722評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤魏颓,失蹤者是張志新(化名)和其女友劉穎岭辣,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體甸饱,經(jīng)...
    沈念sama閱讀 44,189評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡沦童,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,519評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了叹话。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片偷遗。...
    茶點(diǎn)故事閱讀 38,654評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖驼壶,靈堂內(nèi)的尸體忽然破棺而出氏豌,到底是詐尸還是另有隱情,我是刑警寧澤辅柴,帶...
    沈念sama閱讀 34,329評(píng)論 4 330
  • 正文 年R本政府宣布箩溃,位于F島的核電站,受9級(jí)特大地震影響碌嘀,放射性物質(zhì)發(fā)生泄漏涣旨。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,940評(píng)論 3 313
  • 文/蒙蒙 一股冗、第九天 我趴在偏房一處隱蔽的房頂上張望霹陡。 院中可真熱鬧,春花似錦止状、人聲如沸烹棉。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,762評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)浆洗。三九已至,卻和暖如春集峦,著一層夾襖步出監(jiān)牢的瞬間伏社,已是汗流浹背抠刺。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,993評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留摘昌,地道東北人速妖。 一個(gè)月前我還...
    沈念sama閱讀 46,382評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像聪黎,于是被迫代替她去往敵國(guó)和親罕容。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,543評(píng)論 2 349

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