iOS內(nèi)存管理(MRC、ARC)深入淺出

內(nèi)存管理方式

首先明確一點(diǎn),無(wú)論在MRC還是ARC情況下,Objective-C采用的是引用計(jì)數(shù)式的內(nèi)存管理方式,這一方式的特點(diǎn):

  • 自己生成的對(duì)象,自己持有。例如:NSObject * __strong obj = [[NSObject alloc]init];。
  • 非自己生成的對(duì)象,自己也能持有。例如:NSMutableArray * __strong array = [NSMutableArray array];。
  • 不再需要自己持有對(duì)象時(shí)釋放。
  • 無(wú)法釋放非自己持有的對(duì)象。
對(duì)象操作 Objective-C方法
生成并持有對(duì)象 alloc/new/copy/mutableCopy等方法
持有對(duì)象 retain方法
釋放對(duì)象 release方法
廢棄對(duì)象 dealloc方法

自己生成的對(duì)象,自己持有

在iOS內(nèi)存管理中有四個(gè)關(guān)鍵字,alloc、new、copy、mutableCopy,自身使用這些關(guān)鍵字產(chǎn)生對(duì)象,那么自身就持有了對(duì)象

    // 使用了alloc分配了內(nèi)存,obj指向了對(duì)象,該對(duì)象本身引用計(jì)數(shù)為1,不需要retain 
    id obj = [[NSObject alloc] init]; 

    // 使用了new分配了內(nèi)存,objc指向了對(duì)象,該對(duì)象本身引用計(jì)數(shù)為1,不需要retain 
    id obj = [NSObject new];
    

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

    // NSMutableArray通過類方法array產(chǎn)生了對(duì)象(并沒有使用alloc、new、copy、mutableCopt來產(chǎn)生對(duì)象),因此該對(duì)象不屬于obj自身產(chǎn)生的
    // 因此,需要使用retain方法讓對(duì)象計(jì)數(shù)器+1,從而obj可以持有該對(duì)象(盡管該對(duì)象不是他產(chǎn)生的)
    id obj = [NSMutableArray array];
    [obj retain];
    

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

    id obj = [NSMutableArray array];  
    [obj retain];

    // 當(dāng)obj不在需要持有的對(duì)象,那么,obj應(yīng)該發(fā)送release消息
    [obj release];
    
    // 釋放了對(duì)象還進(jìn)行釋放,會(huì)導(dǎo)致奔潰
    [obj release];

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

    // 釋放一個(gè)不屬于自己的對(duì)象
    id obj = [NSMutableArray array]; 
    
    // obj沒有進(jìn)行retain操作而進(jìn)行release操作,然后autoreleasePool也會(huì)對(duì)其進(jìn)行一次release操作,導(dǎo)致奔潰。
    后面會(huì)講到autorelease。
    [obj release];

針對(duì)[NSMutableArray array]方法取得的對(duì)象存在,自己卻不持有對(duì)象,底層大致實(shí)現(xiàn):

+ (id)object {
    //自己持有對(duì)象
    id obj = [[NSObject alloc]init];
    
    [obj autorelease];
    
    //取得的對(duì)象存在,但自己不持有對(duì)象
    return obj;
}

使用了autorelease方法,將obj注冊(cè)到autoreleasePool中,不會(huì)立即釋放,當(dāng)pool結(jié)束時(shí)再自動(dòng)調(diào)用release。這樣達(dá)到取得的對(duì)象存在,自己不持有對(duì)象。

autorelease

顧名思義:autorelease就是自動(dòng)釋放,它和C語(yǔ)言的局部變量類似,C語(yǔ)言局部變量在程序執(zhí)行時(shí),超出其作用域時(shí),會(huì)被自動(dòng)廢棄。autorelease會(huì)像C語(yǔ)言局部變量那樣對(duì)待對(duì)象實(shí)例,當(dāng)超出作用域時(shí),對(duì)象實(shí)例的release實(shí)例方法會(huì)被調(diào)用。我們可以設(shè)定autorelease的作用域。

autorelease具體使用方法

  • 生成并持有NSAutoreleasePool對(duì)象
  • 調(diào)用已分配對(duì)象的autorelease實(shí)例方法
  • 廢棄NSAutoreleasePool對(duì)象
NSAutoreleasePool生命周期

NSAutoreleasePool對(duì)象的生命周期就是一個(gè)作用域,對(duì)于所有調(diào)用過autorelease實(shí)例方法的對(duì)象,在廢棄NSAutoreleasePool對(duì)象時(shí),都會(huì)調(diào)用其release實(shí)例方法。如上圖所示。

用源代碼表示如下:

    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    id obj = [[NSObject alloc] init];
    [obj autorelease];
    [pool drain];

在[pool drain]調(diào)用時(shí),NSAutoreleasePool被銷毀,obj的release方法會(huì)被觸發(fā)。obj被釋放。

autorelease蘋果實(shí)現(xiàn)

可以通過objc4庫(kù)的runtime/objc-arr.mm來確認(rèn)蘋果中autorelease的實(shí)現(xiàn)。

    class AutoreleasePoolPage 
    {
        static inline void *push() 
        {
            相當(dāng)于生成或持有NSAutoreleasePool類對(duì)象
        }

        static inline void *pop(void *token)
        {
            相當(dāng)于廢棄NSAutoreleasePool類對(duì)象
            releaseAll();
        }

        static inline id autorelease(id obj)
        {
            相當(dāng)于NSAutoreleasePool類的addObject類方法   
            AutoreleasePoolPage *autoreleasePoolPage = 取得正在使用的AutoreleasePoolPage實(shí)例; 
            autoreleasePoolPage->add(obj);
        }

        id *add(id obj) 
        {
            將對(duì)象追加到內(nèi)部數(shù)組中
        }

        void releaseAll() 
        {
            調(diào)用內(nèi)部數(shù)組中對(duì)象的release實(shí)例方法 
        }
    };

    void *objc_autoreleasePoolPush(void)
    {
        return AutoreleasePoolPage::push();
    }

    void objc_autoreleasePoolPop(void *ctxt)
    {
        AutoreleasePoolPage::pop(ctxt);
    }

    id *objc_autorelease(id obj) 
    {
        return AutoreleasePoolPage::autorelease(obj);
    }


我們使用調(diào)速器來觀察NSAutoreleasePool類方法和autorelease方法的運(yùn)行過程,如下所示,這些方法調(diào)用了關(guān)聯(lián)于objc4庫(kù)autorelease實(shí)現(xiàn)的函數(shù)。

    //等同于objc_autoreleasePoolPush()
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

    id obj = [[NSObject alloc] init];
    
    //等同于objc_autorelease(obj)
    [obj autorelease];
    
    //等同于objc_autoreleasePoolPop(pool)
    [pool drain];

在iOS程序啟動(dòng)后,主線程會(huì)自動(dòng)創(chuàng)建一個(gè)RunLoop,蘋果在主線程 RunLoop 里注冊(cè)了兩個(gè) Observer,其回調(diào)都是 _wrapRunLoopWithAutoreleasePoolHandler()。

第一個(gè) Observer 監(jiān)視的事件是 Entry(即將進(jìn)入Loop),其回調(diào)內(nèi)會(huì)調(diào)用 _objc_autoreleasePoolPush() 創(chuàng)建自動(dòng)釋放池。其 order 是-2147483647,優(yōu)先級(jí)最高,保證創(chuàng)建釋放池發(fā)生在其他所有回調(diào)之前。

第二個(gè) Observer 監(jiān)視了兩個(gè)事件: BeforeWaiting(準(zhǔn)備進(jìn)入休眠) 時(shí)調(diào)用_objc_autoreleasePoolPop()_objc_autoreleasePoolPush() 釋放舊的池并創(chuàng)建新池;Exit(即將退出Loop) 時(shí)調(diào)用 _objc_autoreleasePoolPop() 來釋放自動(dòng)釋放池。這個(gè) Observer 的 order 是 2147483647,優(yōu)先級(jí)最低,保證其釋放池子發(fā)生在其他所有回調(diào)之后。

在主線程執(zhí)行的代碼,通常是寫在諸如事件回調(diào)、Timer回調(diào)內(nèi)的。這些回調(diào)會(huì)被 RunLoop 創(chuàng)建好的 AutoreleasePool 環(huán)繞著,所以不會(huì)出現(xiàn)內(nèi)存泄漏,開發(fā)者也不一定非得顯示創(chuàng)建 Pool 了。runloop相關(guān)

盡管如此,在大量產(chǎn)生autorelease的對(duì)象時(shí),只要NSAutoreleasePool沒有被廢棄,那么產(chǎn)生的對(duì)象就不能被釋放,因此會(huì)產(chǎn)生內(nèi)存不足的現(xiàn)象。例如:

讀入大量圖像的同時(shí)改變其尺寸。圖像文件讀入到NSData對(duì)象,并從中生成UIImage對(duì)象,改變?cè)搶?duì)象尺寸后生成新的UIImage對(duì)象。這種情況會(huì)產(chǎn)生大量autorelease對(duì)象。

    for (int i = 0; i < 圖片數(shù); ++ i) {
        /*
         讀入圖片
         大量產(chǎn)生autorelease的對(duì)象
         由于沒有廢棄NSAutoreleasePool對(duì)象
         導(dǎo)致產(chǎn)生內(nèi)存峰值,可能內(nèi)存不足而奔潰
         */
    }

在這種情況下,應(yīng)當(dāng)在適當(dāng)?shù)奈恢蒙?、持有或廢棄NSAutoreleasePool對(duì)象

    for (int i = 0; i < 圖片數(shù); ++ i) {
        NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
        /*
         讀入圖片
         大量產(chǎn)生autorelease的對(duì)象
         */
        [pool drain];
        /*
         通過釋放pool
         autorelease的對(duì)象被release,就不會(huì)產(chǎn)生內(nèi)存峰值
         */
    }

使用容器的block版本的枚舉器時(shí),內(nèi)部會(huì)自動(dòng)添加一個(gè)AutoreleasePool:

    [array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
    // 這里被一個(gè)局部@autoreleasepool包圍著
    }];

所有權(quán)修飾符及其原理

在 ARC 特性下有 4 種與內(nèi)存管理息息相關(guān)的變量所有權(quán)修飾符值得我們關(guān)注:

  • __strong
  • __weak
  • __unsafe_unretaied
  • __autoreleasing

說到變量所有權(quán)修飾符,有人可能會(huì)跟屬性修飾符搞混,這里做一個(gè)對(duì)照關(guān)系小結(jié):

  • assign 對(duì)應(yīng)的所有權(quán)類型是 __unsafe_unretained
  • copy 對(duì)應(yīng)的所有權(quán)類型是 __strong。
  • retain 對(duì)應(yīng)的所有權(quán)類型是 __strong。
  • strong 對(duì)應(yīng)的所有權(quán)類型是 __strong。
  • unsafe_unretained對(duì)應(yīng)的所有權(quán)類型是__unsafe_unretained。
  • weak 對(duì)應(yīng)的所有權(quán)類型是 __weak。

__strong

__strong 表示強(qiáng)引用,對(duì)應(yīng)定義 property 時(shí)用到的 strong。當(dāng)對(duì)象沒有任何一個(gè)強(qiáng)引用指向它時(shí),它才會(huì)被釋放。如果在聲明引用時(shí)不加修飾符,那么引用將默認(rèn)是強(qiáng)引用。當(dāng)需要釋放強(qiáng)引用指向的對(duì)象時(shí),需要保證所有指向?qū)ο髲?qiáng)引用置為 nil。__strong 修飾符是 id 類型和對(duì)象類型默認(rèn)的所有權(quán)修飾符。

原理:

    {
        id __strong obj = [[NSObject alloc] init];
    }
    //編譯器的模擬代碼
    id obj = objc_msgSend(NSObject,@selector(alloc));
    objc_msgSend(obj,@selector(init));

    // 出作用域的時(shí)候調(diào)用
    objc_release(obj);

雖然ARC有效時(shí)不能使用release方法,但由此可知編譯器自動(dòng)插入了release。

對(duì)象是通過除alloc、new、copy、multyCopy外方法產(chǎn)生的情況

    {
        id __strong obj = [NSMutableArray array];
    }

結(jié)果與之前稍有不同:

    //編譯器的模擬代碼
    id obj = objc_msgSend(NSMutableArray,@selector(array));
    objc_retainAutoreleasedReturnValue(obj);
    objc_release(obj);

objc_retainAutoreleasedReturnValue函數(shù)主要用于優(yōu)化程序的運(yùn)行。它是用于持有(retain)對(duì)象的函數(shù),它持有的對(duì)象應(yīng)為返回注冊(cè)在autoreleasePool中對(duì)象的方法,或是函數(shù)的返回值。像該源碼這樣,在調(diào)用array類方法之后,由編譯器插入該函數(shù)。

而這種objc_retainAutoreleasedReturnValue函數(shù)是成對(duì)存在的,與之對(duì)應(yīng)的函數(shù)是objc_autoreleaseReturnValue。它用于array類方法返回對(duì)象的實(shí)現(xiàn)上。下面看看NSMutableArray類的array方法通過編譯器進(jìn)行了怎樣的轉(zhuǎn)換:

    + (id)array 
    {
        return [[NSMutableArray alloc] init];
    }
    //編譯器模擬代碼
    + (id)array 
    {
        id obj = objc_msgSend(NSMutableArray,@selector(alloc));
        objc_msgSend(obj,@selector(init));
        
        // 代替我們調(diào)用了autorelease方法
        return objc_autoreleaseReturnValue(obj);
    }

我們可以看見調(diào)用了objc_autoreleaseReturnValue函數(shù)且這個(gè)函數(shù)會(huì)返回注冊(cè)到自動(dòng)釋放池的對(duì)象,但是,這個(gè)函數(shù)有個(gè)特點(diǎn),它會(huì)查看調(diào)用方的命令執(zhí)行列表,如果發(fā)現(xiàn)接下來會(huì)調(diào)用objc_retainAutoreleasedReturnValue則不會(huì)將返回的對(duì)象注冊(cè)到autoreleasePool中而僅僅返回一個(gè)對(duì)象。達(dá)到了一種最優(yōu)效果。如下圖:

省略了autoreleasePool注冊(cè)

__weak

__weak 表示弱引用,對(duì)應(yīng)定義 property 時(shí)用到的 weak。弱引用不會(huì)影響對(duì)象的釋放,而當(dāng)對(duì)象被釋放時(shí),所有指向它的弱引用都會(huì)自定被置為 nil,這樣可以防止野指針。使用__weak修飾的變量,即是使用注冊(cè)到autoreleasePool中的對(duì)象。__weak 最常見的一個(gè)作用就是用來避免循環(huán)循環(huán)。需要注意的是,__weak 修飾符只能用于 iOS5 以上的版本,在 iOS4 及更低的版本中使用 __unsafe_unretained 修飾符來代替。

__weak 的幾個(gè)使用場(chǎng)景:

  • 在 Delegate 關(guān)系中防止循環(huán)引用。
  • 在 Block 中防止循環(huán)引用。
  • 用來修飾指向由 Interface Builder 創(chuàng)建的控件。比如:@property (weak, nonatomic) IBOutlet UIButton *testButton;。

原理

    {
        id __weak obj = [[NSObject alloc] init];
    }

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

    id obj;
    id tmp = objc_msgSend(NSObject,@selector(alloc));
    objc_msgSend(tmp,@selector(init));
    objc_initweak(&obj,tmp);
    objc_release(tmp);
    objc_destroyWeak(&object);

對(duì)于__weak內(nèi)存管理也借助了類似于引用計(jì)數(shù)表的散列表,它通過對(duì)象的內(nèi)存地址做為key,而對(duì)應(yīng)的__weak修飾符變量的地址作為value注冊(cè)到weak表中,在上述代碼中objc_initweak就是完成這部分操作,而objc_destroyWeak
則是銷毀該對(duì)象對(duì)應(yīng)的value。當(dāng)指向的對(duì)象被銷毀時(shí),會(huì)通過其內(nèi)存地址,去weak表中查找對(duì)應(yīng)的__weak修飾符變量,將其從weak表中刪除。所以,weak在修飾只是讓weak表增加了記錄沒有引起引用計(jì)數(shù)表的變化.

對(duì)象通過objc_release釋放對(duì)象內(nèi)存的動(dòng)作如下:

  1. objc_release
  2. 因?yàn)橐糜?jì)數(shù)為0所以執(zhí)行dealloc
  3. _objc_rootDealloc
  4. objc_dispose
  5. objc_destructInstance
  6. objc_clear_deallocating

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

  1. 從weak表中獲取已廢棄對(duì)象內(nèi)存地址對(duì)應(yīng)的所有記錄
  2. 將已廢棄對(duì)象內(nèi)存地址對(duì)應(yīng)的記錄中所有以weak修飾的變量都置為nil
  3. 從weak表刪除已廢棄對(duì)象內(nèi)存地址對(duì)應(yīng)的記錄
  4. 根據(jù)已廢棄對(duì)象內(nèi)存地址從引用計(jì)數(shù)表中找到對(duì)應(yīng)記錄刪除

據(jù)此可以解釋為什么對(duì)象被銷毀時(shí)對(duì)應(yīng)的weak指針變量全部都置為nil,同時(shí),也看出來銷毀weak步驟較多,如果大量使用weak的話會(huì)增加CPU的負(fù)荷。

還需要確認(rèn)一點(diǎn)是:使用__weak修飾符的變量,即是使用注冊(cè)到autoreleasePool中的對(duì)象。

    {
        id __weak obj1 = obj; 
        NSLog(@"obj2-%@",obj1);
    }

編譯器轉(zhuǎn)換上述代碼如下:

    id obj1; 
    objc_initweak(&obj1,obj);
    id tmp = objc_loadWeakRetained(&obj1);
    objc_autorelease(tmp);
    NSLog(@"%@",tmp);
    objc_destroyWeak(&obj1);

objc_loadWeakRetained函數(shù)獲取附有__weak修飾符變量所引用的對(duì)象并retain, objc_autorelease函數(shù)將對(duì)象放入autoreleasePool中,據(jù)此當(dāng)我們?cè)L問weak修飾指針指向的對(duì)象時(shí),實(shí)際上是訪問注冊(cè)到自動(dòng)釋放池的對(duì)象。因此,如果大量使用weak的話,在我們?nèi)ピL問weak修飾的對(duì)象時(shí),會(huì)有大量對(duì)象注冊(cè)到自動(dòng)釋放池,這會(huì)影響程序的性能。

解決方案:要訪問weak修飾的變量時(shí),先將其賦給一個(gè)strong變量,然后進(jìn)行訪問

為什么訪問weak修飾的對(duì)象就會(huì)訪問注冊(cè)到自動(dòng)釋放池的對(duì)象呢?

因?yàn)閣eak不會(huì)引起對(duì)象的引用計(jì)數(shù)器變化,因此,該對(duì)象在運(yùn)行過程中很有可能會(huì)被釋放。所以,需要將對(duì)象注冊(cè)到自動(dòng)釋放池中并在autoreleasePool銷毀時(shí)釋放對(duì)象占用的內(nèi)存。

__unsafe_unretained

ARC 是在 iOS5 引入的,而 __unsafe_unretained 這個(gè)修飾符主要是為了在ARC剛發(fā)布時(shí)兼容iOS4以及版本更低的系統(tǒng),因?yàn)檫@些版本沒有弱引用機(jī)制。這個(gè)修飾符在定義property時(shí)對(duì)應(yīng)的是unsafe_unretained__unsafe_unretained 修飾的指針純粹只是指向?qū)ο?,沒有任何額外的操作,不會(huì)去持有對(duì)象使得對(duì)象的 retainCount +1。而在指向的對(duì)象被釋放時(shí)依然原原本本地指向原來的對(duì)象地址,不會(huì)被自動(dòng)置為 nil,所以成為了野指針,非常不安全。

__unsafe_unretained 的應(yīng)用場(chǎng)景:

在 ARC 環(huán)境下但是要兼容 iOS4.x 的版本,用__unsafe_unretained 替代 __weak 解決強(qiáng)循環(huán)循環(huán)的問題。

__autoreleasing

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

    @autoeleasepool {
        // 如果看了上面__strong的原理,就知道實(shí)際上對(duì)象已經(jīng)注冊(cè)到自動(dòng)釋放池里面了 
        id __autoreleasing obj = [[NSObject alloc] init];
    }

編譯器轉(zhuǎn)換上述代碼如下:

    id pool = objc_autoreleasePoolPush(); 
    id obj = objc_msgSend(NSObject,@selector(alloc));
    objc_msgSend(obj,@selector(init));
    objc_autorelease(obj);
    objc_autoreleasePoolPop(pool);
@autoreleasepool {
        id __autoreleasing obj = [NSMutableArray array];
    }

編譯器轉(zhuǎn)換上述代碼如下:

    id pool = objc_autoreleasePoolPush();
    id obj = objc_msgSend(NSMutableArray,@selector(array));
    objc_retainAutoreleasedReturnValue(obj);
    objc_autorelease(obj);
    objc_autoreleasePoolPop(pool);

上面兩種方式,雖然第二種持有對(duì)象的方法從alloc方法變?yōu)榱?code>objc_retainAutoreleasedReturnValue函數(shù),都是通過objc_autorelease,注冊(cè)到autoreleasePool中。

ARC 模式規(guī)則

ARC 模式下,還有一些需要注意的規(guī)則:

  • 不能顯式使用 retain/release/retainCount/autorelease。
  • 不能使用 NSAllocateObject/NSDeallocateObject。
  • 需要遵守內(nèi)存管理的方法命名規(guī)則。在 ARC 模式和 MRC 模式下,以 alloc/new/copy/mutableCopy 開頭的方法在返回對(duì)象時(shí)都必須返回給調(diào)用方所應(yīng)當(dāng)持有的對(duì)象。在 ARC 模式下,追加一條:以 init 開頭的方法必須是實(shí)例方法并且必須要返回對(duì)象。返回的對(duì)象應(yīng)為 id 類型或聲明該方法的類的對(duì)象類型,或是該類的超類型或子類型。該返回的對(duì)象并不注冊(cè)到 Autorelease Pool 中,基本上只是對(duì) alloc 方法返回值的對(duì)象進(jìn)行初始化處理并返回該對(duì)象。需要注意的是:- (void)initialize; 方法雖然是以 init 開頭但是并不包含在上述規(guī)則中。
  • 不要顯式調(diào)用 dealloc。
  • 使用 @autoreleasepool 塊替代 NSAutoreleasePool。
  • 不能使用區(qū)域(NSZone)。
  • 對(duì)象型變量不能作為 C 語(yǔ)言結(jié)構(gòu)體(struct/union)的成員。
  • 顯式轉(zhuǎn)換 id 和 void *。

Toll-Free Bridging

MRC 下的 Toll-Free Bridging 因?yàn)椴簧婕皟?nèi)存管理的轉(zhuǎn)移,相互之間可以直接交換使用,當(dāng)使用 ARC 時(shí),由于Core Foundation 框架并不支持 ARC,此時(shí)編譯器不知道該如何處理這個(gè)同時(shí)有 ObjC 指針和 CFTypeRef 指向的對(duì)象,所以除了轉(zhuǎn)換類型,還需指定內(nèi)存管理所有權(quán)的改變,可通過 __bridge、__bridge_retainedCFBridgingRetain__bridge_transferCFBridgingRelease。

__bridge

只是聲明類型轉(zhuǎn)變,但是不做內(nèi)存管理規(guī)則的轉(zhuǎn)變。比如:

CFStringRef s1 = (__bridge CFStringRef) [[NSString alloc] initWithFormat:@"Hello, %@!", name];

只是做了 NSString 到 CFStringRef 的轉(zhuǎn)化,但管理規(guī)則未變,依然要用 Objective-C 類型的 ARC 來管理 s1,你不能用 CFRelease() 去釋放 s1。

__bridge_retained or CFBridgingRetain

表示將指針類型轉(zhuǎn)變的同時(shí),將內(nèi)存管理的責(zé)任由原來的 Objective-C 交給Core Foundation 來處理,也就是,將 ARC 轉(zhuǎn)變?yōu)?MRC。比如,還是上面那個(gè)例子

NSString *s1 = [[NSString alloc] initWithFormat:@"Hello, %@!", name];
CFStringRef s2 = (__bridge_retained CFStringRef)s1;
// or CFStringRef s2 = (CFStringRef)CFBridgingRetain(s1);
// do something with s2
//...
CFRelease(s2); // 注意要在使用結(jié)束后加這個(gè)

我們?cè)诘诙凶隽宿D(zhuǎn)化,這時(shí)內(nèi)存管理規(guī)則由 ARC 變?yōu)榱?MRC,我們需要手動(dòng)的來管理 s2 的內(nèi)存,而對(duì)于 s1,我們即使將其置為 nil,也不能釋放內(nèi)存。

__bridge_transfer or CFBridgingRelease

這個(gè)修飾符和函數(shù)的功能和上面那個(gè) __bridge_retained 相反,它表示將管理的責(zé)任由 Core Foundation 轉(zhuǎn)交給 Objective-C,即將管理方式由 MRC 轉(zhuǎn)變?yōu)?ARC。比如:

CFStringRef result = CFURLCreateStringByAddingPercentEscapes(. . .);
NSString *s = (__bridge_transfer NSString *)result;
//or NSString *s = (NSString *)CFBridgingRelease(result);
return s;

這里我們將 result 的管理責(zé)任交給了 ARC 來處理,我們就不需要再顯式地將 CFRelease() 了。

其他

之前寫的和內(nèi)存管理有些許相關(guān):為什么聲明NString,NSArray等需要使用copy,使用strong有什么問題,深拷貝和淺拷貝,block為什么使用copy。

每天搞到晚上兩三點(diǎn),如果對(duì)你有些許幫助的話,給個(gè)小????唄!也歡迎指導(dǎo)溝通。


參考文獻(xiàn)和鏈接:

《Objective-C高級(jí)編程》

終于明白那些年知其然而不知其所以然的iOS內(nèi)存管理方式

深入理解RunLoop

最后編輯于
?著作權(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)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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