《Objective-C高級(jí)編程》ARC規(guī)則

Snip20171212_25.png
1.1 概要

實(shí)際上“引用計(jì)數(shù)式內(nèi)存管理”的本質(zhì)部分在ARC中并沒(méi)有改變室谚。就像“字段引用計(jì)數(shù)”這個(gè)名詞表示的那樣妹懒,ARC只是自動(dòng)地幫助我們處理“引用計(jì)數(shù)”的相關(guān)部分椅亚。

設(shè)置ARC有效的編譯方法如下所示
  • 使用clang(LLVM編譯器)3.0或以上版本
  • 指定編譯器屬性為"-fobjc-arc"
    Xcode4.2 默認(rèn)設(shè)定為對(duì)所有的文件ARC有效
1.2 內(nèi)存管理的思考方式

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

  • 自己生成的對(duì)象顺囊,自己所持有
  • 非自己生成的對(duì)象姻乓,自己也能持有
  • 自己持有的對(duì)象不再需要時(shí)釋放
  • 非自己持有的對(duì)象無(wú)法釋放
1.3 所有權(quán)修飾符

ARC有效時(shí)晰搀,id類(lèi)型和對(duì)象類(lèi)型同C語(yǔ)言器類(lèi)型不同五辽,其類(lèi)型上必須加所有權(quán)修飾符。所有權(quán)修飾符一共有四種厕隧。

  • __strong修飾符
  • __weak修飾符
  • __unsafe_unretained修飾符
  • __autoreleasing修飾符
1.3.1 __strong 修飾符

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

id obj =[ [NSObject alloc] init];
id __strong obj = [[NSObject alloc] init];

id和對(duì)象類(lèi)型在沒(méi)有明確指定所有權(quán)修飾符時(shí),默認(rèn)為_(kāi)_strong修飾符吁讨,上面兩行代碼相同髓迎。

  1. 附有__strong修飾符的變量obj在超出其變量作用域時(shí),即在改變量被廢棄時(shí)建丧,會(huì)釋放其被賦予的對(duì)象排龄。
  2. __strong修飾符的變量,不僅只在變量作用域中翎朱,在賦值上也能夠正確地管理其對(duì)象的所有者橄维。
1.3.2 __weak修飾符

循環(huán)引用容易發(fā)生內(nèi)存泄漏。所謂內(nèi)存泄漏就是應(yīng)當(dāng)廢棄的對(duì)象在超出其生存周期后繼續(xù)存在 拴曲。

  1. __weak修飾符與__strong修飾符相反争舞。弱引用不能持有對(duì)象實(shí)例
    id __weak obj1 = nil;
    {
        id __strong obj0 = [[NSObject alloc] init];
        obj1 = obj0;
        NSLog(@"obj1=%@",obj1);
    }
    
    NSLog(@"obj1=%@",obj1);
執(zhí)行結(jié)果:
obj1=<NSObject: 0x604000002010>
obj1=(null)
  1. __weak修飾符另一個(gè)優(yōu)點(diǎn),在持有對(duì)象的弱引用時(shí)澈灼,若該對(duì)象被廢棄竞川,則此弱引用將自動(dòng)失效且處于nil被賦值的狀態(tài)店溢。

使用__weak修飾符可以避免循環(huán)引用。通過(guò)檢查附有__weak修飾符的變量是否為nil委乌,可以判斷被賦值的對(duì)象是否已經(jīng)被廢棄床牧。

1.3.3 __unsafe_unretained修飾符

__unsafe_unretained修飾符正如其名unsafe所示,是不安全的所有權(quán)修飾符遭贸。盡管ARC式的內(nèi)存管理是編譯器的工作戈咳,但附有__unsafe_unretained修飾符的變量不屬于編譯器的內(nèi)存管理對(duì)象。這一點(diǎn)在使用時(shí)要注意壕吹。

  1. 附有__unsafe_unretained修飾符的變量同附有__weak修飾符的變量一樣著蛙,因?yàn)樽约荷刹⒊钟械膶?duì)象不能繼續(xù)為自己所有,所以生成的對(duì)象會(huì)立即被釋放算利。
  2. 在iOS4以及OS X Snow Leopard的應(yīng)用程序中册踩,必須使用__unsafe_unretained修飾符來(lái)替代__weak修飾符泳姐。賦值給附有__unsafe_unretained修飾符變量的對(duì)象在通過(guò)該變量使用時(shí)效拭,如果沒(méi)有確保其確實(shí)存在,那么應(yīng)用程序會(huì)奔潰
1.3.4 __autoreleasing 修飾符

在ARC有效時(shí)胖秒,用@autoreleasepool塊替代NSAtoreleasePool類(lèi)缎患,用附有__autoreleasing修飾符的變量替代autorelease方法。

Snip20171209_14.png

為什么在訪問(wèn)附有__weak修飾符的變量時(shí)必須訪問(wèn)注冊(cè)到autoreleasepool的對(duì)象阎肝?因?yàn)開(kāi)_weak修飾符只持有對(duì)象的弱引用挤渔,而在訪問(wèn)對(duì)象的過(guò)程中,該對(duì)象有可能被廢棄风题。如果把要訪問(wèn)的對(duì)象注冊(cè)到autoreleasepool中判导,那么@autoreleasepool塊結(jié)束之前都能確保該對(duì)象存在。因此沛硅,在使用附有__weak修飾符的變量時(shí)必定要使用注冊(cè)到autoreleasepool中的對(duì)象眼刃。
專(zhuān)欄 __strong 修飾符/__weak 修飾符
附有__strong修飾符,__weak修飾符的變量類(lèi)似有C++中智能指針std::shared_ptr和std::weak_ptr。std::shared_ptr可通過(guò)引用計(jì)數(shù)來(lái)持有C++類(lèi)實(shí)例摇肌,std::weak_ptr可避免循環(huán)引用擂红。在不得不使用沒(méi)有__strong 修飾符/__weak 修飾符的C++時(shí),強(qiáng)烈推薦使用這兩種智能指針围小。

2.1 規(guī)則

在ARC有效的情況下編譯源代碼昵骤,必須遵守一定的規(guī)則。

  • 不能使用retain/release/retainCount/autorelease
  • 不能使用NSAllocateObject/NSDeallocateObject
  • 必須遵守內(nèi)存管理的方法命名規(guī)則
  • 不要顯示調(diào)用dealloc
  • 使用@autoreleaepool塊替代NSAutoreleasePool
  • 不能使用區(qū)域(NSZone)
  • 對(duì)象型變量不能作為C語(yǔ)言結(jié)構(gòu)體(struct/union)的成員
  • 顯示轉(zhuǎn)換“id”和“void”
2.1.1 不能使用retain/release/retainCount/autorelease

內(nèi)存管理是編譯器的工作肯适,因此沒(méi)有必要使用內(nèi)存管理的方法变秦。

設(shè)置ARC有效時(shí),如果編譯器使用了這些方法的源代碼框舔,會(huì)報(bào)錯(cuò)
設(shè)置ARC有效時(shí)蹦玫,禁止再次鍵入retain或者是release代碼
只能在ARC無(wú)效并且手動(dòng)進(jìn)行內(nèi)存管理時(shí)使用retain/release/retainCount/autolease方法

2.1.2 不能使用NSAllocateObject/NSDeallocateObject

在ARC有效時(shí),禁止使用NSAllocateObject函數(shù),如果使用會(huì)引起編譯器錯(cuò)誤

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

在ARC無(wú)效時(shí)钳垮,用于對(duì)象生成/持有的方法必須遵守以下的命名規(guī)則

  • alloc
  • new
  • copy
  • mutableCopy
2.1.4 不要顯示調(diào)用dealloc

無(wú)論ARC是否有效惑淳,只要對(duì)象的所有者都不持有該對(duì)象,該對(duì)象就被廢棄饺窿。對(duì)象被廢棄時(shí)歧焦,不管ARC是否有效,都會(huì)調(diào)用對(duì)象的dealloc方法肚医。

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

C語(yǔ)言的結(jié)構(gòu)體(struct或union)成員中绢馍,如果存在Objective-C對(duì)象型變量,會(huì)引起編譯器錯(cuò)誤肠套。

2.1.6 顯示轉(zhuǎn)換id和void *

在ARC有效時(shí)舰涌,將id變量強(qiáng)制轉(zhuǎn)換void *變量會(huì)引起編譯器錯(cuò)誤

id obj = [[NSObject alloc] init];
void *p = obj;

更進(jìn)一步,將void *變量賦值給id變量你稚,調(diào)用起實(shí)例方法瓷耙,編譯器會(huì)報(bào)錯(cuò)

id o = p;
[o release];

id型或?qū)ο笮妥兞抠x值給void *或者逆向賦值時(shí)都需要進(jìn)行特定的轉(zhuǎn)換。如果只想單純地賦值刁赖,額可以使用"__bridge"轉(zhuǎn)換搁痛。

id obj = [[NSObject alloc] init];
void *p = (__bridge void *)obj;
id o = (__bridge id)p;
專(zhuān)欄 Objective-C對(duì)象與Core Foundation對(duì)象
  1. Core Foundation對(duì)象主要使用在用C語(yǔ)言編寫(xiě)的Core Foundation框架中,并使用引用計(jì)數(shù)的對(duì)象宇弛。在ARC無(wú)效時(shí)鸡典,Core Foundation框架中的retain/release分別是CFRetain/CFRelease。
  2. Core Foundation對(duì)象與Objective-C對(duì)象的區(qū)別很小枪芒,不同之處只在于是由哪一個(gè)框架所生成的彻况。無(wú)論是由哪種框架生成的對(duì)象,一旦生成后舅踪,便能在不同的框架中使用纽甘。Foundation框架的API生成并持有的對(duì)象可以用Core Foundation框架的API釋放。反過(guò)來(lái)也可以硫朦。
  3. 因?yàn)镃ore Foundation對(duì)象與Objective-C對(duì)象沒(méi)有區(qū)別贷腕。所以在ARC無(wú)效時(shí),只用簡(jiǎn)單的C語(yǔ)言的轉(zhuǎn)換也能實(shí)現(xiàn)互換咬展。另外這種轉(zhuǎn)換不需要使用額外的CPU資源泽裳。因此被稱(chēng)為“免費(fèi)橋”。
3.1 屬性

當(dāng)ARC有效時(shí)破婆,以下可作為這種屬性聲明中使用的屬性來(lái)用

屬性聲明的屬性 所有權(quán)修飾符
assign __unsafe_unretained修飾符
copy __strong 修飾符(但是負(fù)責(zé)的是被復(fù)制的對(duì)象)
retain __strong修飾符
strong __strong修飾符
unsafe_unretained __unsafe_unretained修飾符
weak __weak修飾符

在聲明類(lèi)成員變量時(shí)涮总,如果同屬性聲明中的屬性不一致會(huì)引起編譯錯(cuò)誤。如下例

@property(nonatomic, weak) id obj; // 編譯器報(bào)錯(cuò)
@property(nonatomic, weak) id __weak obj;  // 正確祷舀,附加__weak修飾符
@property(nonatomic, strong) id obj;  // 正確瀑梗,使用strong
4.1 數(shù)組

如下例代碼烹笔,將nil代入到malloc函數(shù)所分配的數(shù)組各元素中來(lái)初始化是非常危險(xiǎn)的。

arrar = (id __strong *)malloc(sizeof(id) * entries);
for (NSUInteger i = 0; i < entries; i++)
  array[i] = nil; 

這是因?yàn)橛蒻alloc函數(shù)分配的內(nèi)存區(qū)域中沒(méi)有被初始化為0抛丽,因此nil會(huì)被賦值給附有__strong修飾符的并被賦值了隨機(jī)的變量中谤职,從而釋放一個(gè)不存在的對(duì)象。在分配內(nèi)存時(shí)推薦使用calloc函數(shù)亿鲜。
像這樣允蜈,通過(guò)calloc函數(shù)分配的動(dòng)態(tài)數(shù)組就能完全像靜態(tài)數(shù)組一樣使用。

array[0] = [[NSObject alloc] init];

但是蒿柳,在動(dòng)態(tài)數(shù)組中操作附有__strong修飾符的變量與靜態(tài)數(shù)組有很大差異饶套,需要自己釋放所有的元素。
如以下源代碼所示垒探,在只是簡(jiǎn)單地用free函數(shù)廢棄了數(shù)組用內(nèi)存塊的情況下妓蛮,數(shù)組各元素所賦值的對(duì)象不能再次釋放,從而引起內(nèi)存泄漏圾叼。

free(array);

這是因?yàn)樵陟o態(tài)數(shù)組中蛤克,編譯器能夠根據(jù)變量的作用域自動(dòng)插入釋放賦值對(duì)象的代碼,從而動(dòng)態(tài)數(shù)組中褐奥,編譯器不能確定數(shù)組的生存周期咖耘,所以無(wú)從處理。如以下源代碼所示撬码,一定要將nil賦值給所有元素中,使得元素所賦值對(duì)象的強(qiáng)引用失效版保,從而釋放那些對(duì)象呜笑。在此之后,使用free函數(shù)廢棄內(nèi)存塊彻犁。

for (NSUInteger i = 0; i < entries; i++)
    array[i] = nil;
free(array);

同初始化時(shí)的注意事項(xiàng)相反叫胁,即使用memset等函數(shù)將內(nèi)存填充為0也不會(huì)釋放所賦值的對(duì)象。這非常危險(xiǎn)汞幢,只會(huì)引起內(nèi)存泄漏驼鹅。對(duì)于編譯器,必須明確地使用賦值給附有__strong修飾符變量的源代碼森篷,所以請(qǐng)注意输钩,必須將nil賦值給所有數(shù)組元素。

使用memcpy函數(shù)拷貝數(shù)組元素以及realloc函數(shù)重新分配內(nèi)存塊也會(huì)有危險(xiǎn)仲智。由于數(shù)組元素所賦值的對(duì)象有可能被保留在內(nèi)存中或是重復(fù)被廢棄买乃,所以這兩個(gè)函數(shù)也禁止使用。

5.1 ARC的實(shí)現(xiàn)
5.1.1 __strong 修飾符
Snip20171213_29.png
5.1.2 __weak修飾符
  • 若附有__weak修飾符的變量所引用的對(duì)象唄廢棄钓辆,則將nil賦值給該變量
  • 使用附有__weak修飾符的變量剪验,即是使用注冊(cè)到autoreleasepool中的對(duì)象肴焊。
5.1.3 __autoreleasing修飾符

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

6.1 引用計(jì)數(shù)
uintptr_t _objc_rootRetainCount(id obj)

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

id __strong obj = [[NSObject alloc] init];
NSLog(@"retain count = %d",_objc_rootRetainCount(obj));

不使用__autoreleasing修飾符娶眷,僅使用附有__weak聲明的變量也能將引用對(duì)象注冊(cè)到autoreleasepool中。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末啸臀,一起剝皮案震驚了整個(gè)濱河市茂浮,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌壳咕,老刑警劉巖席揽,帶你破解...
    沈念sama閱讀 218,640評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異谓厘,居然都是意外死亡幌羞,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,254評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)竟稳,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)属桦,“玉大人,你說(shuō)我怎么就攤上這事他爸∧舯觯” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,011評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵诊笤,是天一觀的道長(zhǎng)系谐。 經(jīng)常有香客問(wèn)我,道長(zhǎng)讨跟,這世上最難降的妖魔是什么纪他? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,755評(píng)論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮晾匠,結(jié)果婚禮上茶袒,老公的妹妹穿的比我還像新娘。我一直安慰自己凉馆,他們只是感情好薪寓,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,774評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著澜共,像睡著了一般向叉。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上咳胃,一...
    開(kāi)封第一講書(shū)人閱讀 51,610評(píng)論 1 305
  • 那天植康,我揣著相機(jī)與錄音,去河邊找鬼展懈。 笑死销睁,一個(gè)胖子當(dāng)著我的面吹牛供璧,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播冻记,決...
    沈念sama閱讀 40,352評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼睡毒,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了冗栗?” 一聲冷哼從身側(cè)響起演顾,我...
    開(kāi)封第一講書(shū)人閱讀 39,257評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎隅居,沒(méi)想到半個(gè)月后钠至,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,717評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡胎源,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,894評(píng)論 3 336
  • 正文 我和宋清朗相戀三年棉钧,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片涕蚤。...
    茶點(diǎn)故事閱讀 40,021評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡宪卿,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出万栅,到底是詐尸還是另有隱情佑钾,我是刑警寧澤,帶...
    沈念sama閱讀 35,735評(píng)論 5 346
  • 正文 年R本政府宣布烦粒,位于F島的核電站休溶,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏撒遣。R本人自食惡果不足惜邮偎,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,354評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望义黎。 院中可真熱鬧,春花似錦豁跑、人聲如沸廉涕。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,936評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)狐蜕。三九已至,卻和暖如春卸夕,著一層夾襖步出監(jiān)牢的瞬間层释,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,054評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工快集, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留贡羔,地道東北人廉白。 一個(gè)月前我還...
    沈念sama閱讀 48,224評(píng)論 3 371
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像乖寒,于是被迫代替她去往敵國(guó)和親猴蹂。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,974評(píng)論 2 355

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