iOS 內(nèi)存管理(二)ARC屬性修飾符

ARC在編譯期間,根據(jù)Objective-C對(duì)象的存活周期,在適當(dāng)?shù)奈恢锰砑觬etain和release代碼。從概念上講,ARC與手動(dòng)引用計(jì)數(shù)內(nèi)存管理遵循同樣的內(nèi)存管理規(guī)則,但是ARC也無法防止循環(huán)強(qiáng)引用。

ARC模式下的修飾符來修飾變量和聲明屬性:

  • 聲明變量的修飾符:__strong, __weak, __unsafe_unretained, __autoreleasing
  • 聲明屬性的修飾符:strong, weak, unsafe_unretained,assign
  • 對(duì)象和CoreFoundation對(duì)象直接的轉(zhuǎn)換修飾符號(hào):__bridge,__bridge_retained或CFBridgingRetain, __bridge_transfer或CFBridgingRelease。
  • 對(duì)于線程的安全,有nonatomic,這樣效率就更高了,但是不是線程的。如果要線程安全,可以使用atomic,這樣在訪問是就會(huì)有線程鎖。

這里主講iOS ARC所以就只講weak strong assign這三個(gè)修飾符,__weak __strong __assign跟前面的一樣

__strong 修飾符

先來看一段代碼:

NSObject *obj1 = [[NSObject alloc]init];
id obj2 = obj1;

__strong 修飾符是id類型和對(duì)象類型默認(rèn)的所有權(quán)修飾符,也就是說上面的變量,實(shí)際上附加了所有權(quán)修飾符,id和對(duì)象類型在沒有明確的指定所有權(quán)修飾符時(shí),默認(rèn)為__strong修飾符,上面代碼實(shí)質(zhì)上跟下面的代碼一致

__strong NSObject *obj1 = [[NSObject alloc]init];
__strong id obj2 = obj1;

通過__strong修飾符,不必再次鍵入retain或者release,完美地滿足了“引用計(jì)數(shù)內(nèi)存管理的思考方式”:</p>
自己生成的對(duì)象,自己持有
非自己生成的對(duì)象,自己也能持有
不再需要自己持有的對(duì)象時(shí)釋放
非自己持有的對(duì)象無法釋放

事列代碼

NSObject *obj1 = [[NSObject alloc]init];
NSObject *obj2 = obj1;
NSLog(@"obj1:%@,obj2=%@",obj1,obj2);
obj1 = nil;
NSLog(@"obj1:%@,obj2=%@",obj1,obj2);

輸出為:<p>
obj1:<NSObject: 0x100203ab0>,obj2=<NSObject: 0x100203ab0></p>
obj1:(null),obj2=<NSObject: 0x100203ab0>

__weak修飾符

看起來好像通過 __strong 修飾符編譯器就能完美地進(jìn)行內(nèi)存管理,大師遺憾的是,僅通過__strong修飾符時(shí)不能解決有些重大問題的。
這里提到的重大問題就是引用計(jì)數(shù)試內(nèi)存管理中必然會(huì)發(fā)生的“循環(huán)引用”的問題

image

例如前面出現(xiàn)的帶有__strong修飾符的成員變量在持有對(duì)象時(shí),很容易發(fā)生循環(huán)引用

@interface Test : NSobject {

    id __strong _obj;
}
- (void)setObj:(id)obj;
@end
@implementation Test
- (void)setObj:(id)obj {
    _obj = obj;
}
@end
以下就為循環(huán)引用
int main() {
    id test1 = [[Test alloc]init];/*對(duì)象a*/
    //test1持有Test對(duì)象a的強(qiáng)引用
    id test2 = [[Test alloc]init];/*對(duì)象b*/
    //test2持有Test對(duì)象b的強(qiáng)引用
    [test1 setObj:test2];
    /*
    test1對(duì)象a的obj成員變量持有test2對(duì)象b的強(qiáng)引用
    此時(shí),持有test對(duì)象b的強(qiáng)引用的變量為Test對(duì)象a的_obj和test2
    */
    [test2 setObj:test1];
    /*
    test2對(duì)象b的obj成員變量持有test2對(duì)象a的強(qiáng)引用
    此時(shí),持有test對(duì)象b的強(qiáng)引用的變量為Test對(duì)象b的_obj和test1
    */
    /*
    因?yàn)閠est1變量超出其作用域,強(qiáng)引用失效,所以自動(dòng)釋放對(duì)象a
    因?yàn)閠est2變量超出其作用域,強(qiáng)引用失效,所以自動(dòng)釋放對(duì)象b
    此時(shí),持有test對(duì)象a的強(qiáng)引用的變量為test2對(duì)象b的_obj
    此時(shí),持有test對(duì)象b的強(qiáng)引用的變量為test1對(duì)象a的_obj
    內(nèi)存泄漏出現(xiàn)了,兩個(gè)對(duì)象超出作用域都沒有被釋放
    */
    return 0;
}
下面這種情況,雖然只有一個(gè)對(duì)象,但在該對(duì)象持有其自身是也發(fā)生了循環(huán)引用
int main(){
    id test = [[Test alloc]init];
    [test setObj:test];
    //對(duì)自身的強(qiáng)引用,自引用
    return 0;
}
image
__weak id obj = [[NSObject alloc]init];
//實(shí)際上這樣的代碼是會(huì)報(bào)警告
//此代碼將自己生成的并吃藕的對(duì)象賦值給附有 __weak修飾符的變量 obj。即變量obj持有對(duì)持有對(duì)象的弱引用,因此,為了不以自己持有的狀態(tài)來保存自己生成并持有的對(duì)象,生成的對(duì)象會(huì)立即被釋放。編譯器對(duì)此會(huì)給出警告。如果想下面這樣,將對(duì)象賦值給一個(gè)strong修飾符的變量后再賦值給附有weak修飾符的變量,就不會(huì)發(fā)生警告了。
{
    id __strong obj0 = [[NSObject alloc]init];
    id __weak obj1 = obj0;
}

因?yàn)閹_weak修飾符的變量不持有對(duì)象,所以超出其變量作用域是,對(duì)象即唄釋放。如果像下面這樣將先前可能發(fā)生循環(huán)引用的類成員變量改成附有__weak修飾符的成員變量的話,該現(xiàn)象則可以避免

image
@interface Test : NSObject 
{
 id __weak _obj;    
}
@end

__weak修飾符還有另一有點(diǎn)。在持有某對(duì)象的弱引用時(shí),若該對(duì)象被廢棄,則此引用將自動(dòng)失效且處于nil被賦值狀態(tài)(空弱引用)。即下代碼

NSObject *obj1 = [[NSObject alloc]init];
__weak NSObject *obj2 = obj1;
NSLog(@"obj1:%@,obj2=%@",obj1,obj2);
obj1 = nil;
NSLog(@"obj1:%@,obj2=%@",obj1,obj2);

輸出結(jié)果為
obj1:<NSObject: 0x100202e50>,obj2=<NSObject: 0x100202e50>
obj1:(null),obj2=(null)
使用__weak修飾符可避免循環(huán)引用,通過檢查附有__weak修飾符變量
__weak修飾符智能用于iOS5以上及OS XLion以上版本的應(yīng)用程序,iOS4以及OS X Snow Leopard 的應(yīng)用程序中可以使用__unsafe_unretained來代替。

__unsafe_unretained 修飾符

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

id __unsafe_unretained obj = [[NSObject alloc] init];<p>

<html>
該代碼將自己生成并持有的對(duì)象賦值給附有__unsafe_unretained修飾符的變量中,雖然使用了unsafe的變量,但是編譯器并不會(huì)忽略,而是給出適當(dāng)?shù)木妗?lt;p>
</html>

warning:Assigning retained object to unsafe_unretained variable; object will be released after assignment

附有__unsafe_unretained修飾符的變量同附有__weak修飾發(fā)的變量一樣,因?yàn)樽约荷刹⒊钟械膶?duì)象不能繼續(xù)為自己所有,所以生成的對(duì)象會(huì)立即被釋放。到這里,__unsafe_unretained修飾符和__weak修飾符時(shí)一樣的,下面我們來看一看差異。

__unsafe_unretained id obj1 = nil;
({
        id obj2 = [[NSObject alloc] init];
        obj1 = obj2;
        NSLog(@"obj1:%@,obj2:%@",obj1,obj2);
});
NSLog(@"obj1:%@",obj1);

代碼執(zhí)行結(jié)果為

obj1:<NSObject: 0x1003007c0>,obj2:<NSObject: 0x1003007c0>
obj1:<NSObject: 0x1003007c0>

<html>
發(fā)生了什么,來看一看
</html>

         __unsafe_unretained id obj1 = nil;
        ({
            /** 自己生成并持有對(duì)象*/
            id obj2 = [[NSObject alloc] init];
            /** 
             *因?yàn)閛bj2變量為強(qiáng)引用,所以自己持有對(duì)象
             */
            obj1 = obj2;
            /**雖然obj2變量賦值給了obj1,但是obj1變量即不持有對(duì)象的強(qiáng)引用也不持有弱引用
             */
            NSLog(@"obj1:%@,obj2:%@",obj1,obj2);
        });
        /** 因?yàn)閛bj2變量超出其作用域,所以自動(dòng)釋放自己持有的對(duì)象
         * 此時(shí)對(duì)象無持有者,所以廢棄該對(duì)象
         */
        NSLog(@"obj1:%@",obj1);
        /** 
         *輸出obj1比那兩表示的對(duì)象
         *obj1變量表示的對(duì)象已經(jīng)被廢棄(懸垂指針)!錯(cuò)誤訪問!
         */

也就是說,最后一行的NSLog只是碰巧正常運(yùn)行而已。雖然訪問了已經(jīng)被廢棄的對(duì)象,但是應(yīng)用程序在個(gè)別運(yùn)行狀態(tài)才會(huì)崩潰。
在使用__unsafe_unretained修飾符時(shí),賦值給附有__strong修飾符的變量時(shí)有必要確保被賦值的對(duì)象確實(shí)存在。
但是,在使用前,讓我們?cè)谝怀呦胂霝槭裁葱枰褂酶接衉_unsafe_unretained修飾符變量。
比如在iOS4以及OS X Snow Leopard的應(yīng)用程序中,必須使用__unsafe_unretained修飾符來替代__weak修飾符。賦值給附有__unsafe_unretained修飾符變量的對(duì)象在通過該變量使用時(shí),如果沒有確保其實(shí)際存在,那么應(yīng)用程序就會(huì)崩潰。

__autoreleasing修飾符

ARC有效是autorelease會(huì)如何呢,不能使用autorelease方法。另外,也不能使用NSAutoreleasePool類。這樣一來,雖然autorelease無法直接使用,但實(shí)際上,ARC有效時(shí)autorelease功能起作用的

//ARC無效
{
    NSAutoreleasePool *pool = [[NSAutorelease alloc]init];
    id obj = [[NSObject alloc]init];
    [obj autorelease];
    [pool autorelease];
}
//ARC有效
{
    @autoreleasepool {
        id __autoreleasing obj = [[NSObject alloc]init];
    }
}

指定“@autoreleasepool塊”來替代“NSAutoreleasePool類對(duì)象生成、持有以及廢棄”這一范圍。
另外,ARC有效是,要通過將對(duì)象賦值給附加了__autoreleasing修飾符的變量來替代調(diào)用autorelease方法。對(duì)象賦值給附有__autoreleasing對(duì)象修飾符的變量等價(jià)于ARC五小時(shí)調(diào)用對(duì)象那個(gè)的autorelease方法,即對(duì)象被注冊(cè)到autoreleasepool。
也就是說可以理解為,在ARC有效時(shí),用@autoreleasepool塊來替代NSAreleasePool類,用附有__autoreleasing修飾符的變量替代autorelease方法。

image

assign屬性修飾符跟__unsafe_unretained一致,用來修飾基礎(chǔ)數(shù)據(jù)類型

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,001評(píng)論 6 537
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,786評(píng)論 3 423
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,986評(píng)論 0 381
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,204評(píng)論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,964評(píng)論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,354評(píng)論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,410評(píng)論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,554評(píng)論 0 289
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,106評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,918評(píng)論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,093評(píng)論 1 371
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,648評(píng)論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,342評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,755評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,009評(píng)論 1 289
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,839評(píng)論 3 395
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,107評(píng)論 2 375

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