天天看點

ARC記憶體管理機制詳解

        ARC在OC裡面個人感覺又是一個高大上的牛詞,在前面Objective-C中的記憶體管理部分提到了ARC記憶體管理機制,ARC是Automatic Reference Counting---自動引用計數。有自動引用計數,那麼就得有手動引用計數MRC(Mannul Reference Counting),前面已經提到過了MRC。那麼在ARC模式下是不是意味着我們就可以一點也不用進行記憶體管理的呢?并不是這樣的,我們還需要代碼進行記憶體的管理。下面會結合着代碼把OC中的ARC機制做一個詳細的總結(歡迎大家批評指針,轉載請注明出處 )。

        在ARC機制下是少不了下面這些東西的:

            1.關鍵字 __strong  預設值,表示隻要有強引用指針指向該變量,則該變量會一直存在。

            2.關鍵字__weak 弱引用,表示若沒有任何強引用指針指向該變量,會自動将變量的值置為空,即nil狀态。

            3.關鍵字 __autoreleasing 用于标示自動釋放的變量

            4.__unsafe_unretained 不安全的弱引用,若沒有任何強引用指針指向該變量,不會自動設為空,會成為野指針。

   關于Weak和Strong,看下圖吧:

ARC記憶體管理機制詳解

        第一次接觸ARC的小夥伴們看到上面的概念可能會一頭霧水,上面說的是個啥?都是哪跟哪?不用着急,下面會有執行個體代碼,結合着執行個體代碼,然後再做一個總結,你就會有種豁然開朗的感覺。你就會明白,哦,原來ARC是這麼一回事。好啦,廢話少說,用代碼講東西才是王道,代碼走起。(為了友善我們觀察記憶體的釋放情況,可以設定斷點來單步運作)

        為了做測試使用,我們建一個測試類,并重寫dealloc方法來觀察記憶體的釋放情況,測試類如下;

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

#import <Foundation/Foundation.h>

@interface TestClass : NSObject

@property(nonatomic, strong) NSString *name;

@end

#import "TestClass.h"

@implementation TestClass

//dealloc在對象釋放是回調用

-(

void

)dealloc

{

NSLog(@

"%@,對象被釋放啦!"

, _name);

}

@end

        一.__strong:  強引用,是ARC中變量聲明的預設值,用大白話講就是你手動配置設定的堆記憶體,如果沒有指針指向這塊記憶體,那麼這塊記憶體就會被回收

            1.當聲明變量為強引用時,對象的指針出棧時,如果該指針指向的記憶體空間沒有别的指針指向他,就自動掉用dealloc方法釋放堆記憶體測試代碼如下:

//情況1.當指向記憶體的指針在出棧時,記憶體被釋放

void

strongTest1()

{

//測試用的代碼塊

{

//預設為強引用的變量

TestClass *obj1 = [TestClass 

new

];

obj1.name = @

"obj1"

;

}

NSLog(@

"在出上面的大括号時,指針變量被釋放,堆配置設定的記憶體也會别立即釋放"

);

}

            代碼運作結果:

2014-08-13 19:25:52.378 ARCDemo[4345:303] obj1,對象被釋放啦!

2014-08-13 19:25:52.380 ARCDemo[4345:303] 在出上面的大括号時,指針變量被釋放,堆配置設定的記憶體也會别立即釋放

            代碼說明:從運作結果來看,出代碼塊後我們定于的指針變量會随着我們代碼塊的結束而釋放,就沒有指針指向我們配置設定的堆記憶體了,以為預設為strong,是以在ARC機制下會立即調用dealloc來釋放堆記憶體。

        2.給對象指針重寫配置設定記憶體的情況,代碼如下:

//情況2.當對象指針 指向其他記憶體時,原有的記憶體若沒有指針指向他,就會被立即釋放

void

strongTest2()

{

{

TestClass * obj1 = [TestClass 

new

];

obj1.name = @

"obj1"

;

//給已經配置設定記憶體的指針在配置設定新的記憶體

obj1 = [TestClass 

new

];

NSLog(@

"經過上面一步會釋放第一次配置設定的堆記憶體!"

);

obj1.name = @

"obj1_new"

;

}

NSLog(@

"出大括号回釋放第二次配置設定的記憶體"

);

}

2014-08-13 19:30:38.455 ARCDemo[4356:303] obj1,對象被釋放啦!

2014-08-13 19:30:38.456 ARCDemo[4356:303] 經過上面一步會釋放第一次配置設定的堆記憶體!

2014-08-13 19:30:38.457 ARCDemo[4356:303] obj1_new,對象被釋放啦!

2014-08-13 19:30:38.457 ARCDemo[4356:303] 出大括号回釋放第二次配置設定的記憶體

            代碼說明:我們先給strong類型的對象指針配置設定記憶體空間,然後再次配置設定記憶體空間,在第二次配置設定空間的時候,就沒有對象指針指向原有的記憶體空間,是以在第二次配置設定空間之後就會把原有的記憶體空間給釋放掉,在出代碼塊的時候,對象指針也會随着棧記憶體的釋放而釋放掉,也沒有對象指針指向第二次配置設定的記憶體了,是以會被釋放掉。

        3.把對象指針置為空時,配置設定的堆記憶體會立即被釋放掉。相應的代碼如下:

void

strongTest3()

{

{

TestClass * obj = [TestClass 

new

];

obj.name = @

"obj"

;

obj = nil;

NSLog(@

"把指針置空時,指針指向的記憶體空間會被釋放"

);

}

}

2014-08-13 19:42:34.827 ARCDemo[4373:303] obj,對象被釋放啦!

2014-08-13 19:42:34.829 ARCDemo[4373:303] 把指針置空時,指針指向的記憶體空間會被釋放

            代碼說明:把指向該記憶體空間的對象指針置空,就相當于沒有指針指向該記憶體空間,是以在strong下會被立即釋放。

        4.把新的對象指針指向堆記憶體空間,然後把原有的指針進行置空

            代碼如下:

//情況4.把新的對象指針指向堆記憶體空間,然後把原有的指針進行置空

void

strongTest4()

{

{

TestClass * obj1 = [TestClass 

new

];

obj1.name = @

"obj1"

;

TestClass * obj2 = obj1;

obj1 = nil;

NSLog(@

"obj1指向的記憶體不會被釋放,因為還有obj2指向"

);

}

}

            運作結果:

2014-08-13 19:46:06.554 ARCDemo[4394:303] obj1指向的記憶體不會被釋放,因為還有obj2指向

2014-08-13 19:46:06.556 ARCDemo[4394:303] obj1,對象被釋放啦!

            代碼說明:當兩個指針同時指向一塊記憶體空間時,把原有的指針置為空,這塊記憶體空間不會被釋放的,因為還有其他的指針指向該記憶體空間。

    二. __weak 歸零弱引用:在若指針指向的記憶體被釋放後,若引用的指針則會置零

            歸零弱引用:弱引用的指針指向強引用的記憶體時,是不影響其釋放記憶體空間的,當弱引用指針所指空間被釋放掉得時候,該弱引用指針會被置零。

            代碼如下

//weak: 歸零弱引用:在若指針指向的記憶體被釋放後,若引用的指針則會置零

void

weakTest()

{

//定義弱引用指針

__weak TestClass *obj1;

{

//預設為強引用

TestClass *obj2 = [TestClass 

new

];

obj2.name = @

"obj2"

;

//弱引用指針指向obj2

obj1 = obj2;

NSLog(@

"強制引用堆配置設定得記憶體空間被釋放前obj1的位址為:%p"

, obj1);

}

NSLog(@

"強制引用堆配置設定得記憶體空間被釋放後obj1的位址為:%p"

, obj1);

}

        運作結果如下:

2014-08-13 19:55:31.393 ARCDemo[4413:303] 強制引用堆配置設定得記憶體空間被釋放前obj1的位址為:0x100201ea0

2014-08-13 19:55:31.395 ARCDemo[4413:303] obj2,對象被釋放啦!

2014-08-13 19:55:31.395 ARCDemo[4413:303] 強制引用堆配置設定得記憶體空間被釋放後obj1的位址為:0x0

        代碼說明:當出大括号時強引用指針會被釋放掉,之前開辟的堆記憶體空間隻有一個弱引用指針指向他,是以在ARC中會被自動釋放,弱引用指針會置零。

    三. __autoreleasing 自動釋放,一般結合着@autoreleasepool使用。

        1.自動釋放修飾的指針所指向的記憶體空間會在自動釋放池結束的時候會被釋放,代碼如下

//情況1:自動釋放類型和自動釋放池配合,提前釋放對象,會産生野指針

void

autoReleaseTest1()

{

//定義自動釋放對象指針

__autoreleasing TestClass *obj;

//定義釋放池

@autoreleasepool {

obj = [TestClass 

new

];

obj.name = @

"obj"

;

}

//此時obj為野指針

NSLog(@

"obj_p = %p"

,obj);

}

        代碼運作結果:

2014-08-13 20:02:00.489 ARCDemo[4436:303] obj,對象被釋放啦!

2014-08-13 20:02:00.490 ARCDemo[4436:303] obj_p = 0x100108f00

        代碼說明:自動釋放池結束後,自動對象指針指向的記憶體空間會被釋放,但上面的用法會産生野指針。

    2.__autoreleasing結合着自動釋放池會延遲記憶體空間的釋放

        代碼如下:

//情況2.自動釋放類型和自動釋放池配合,延遲對象的釋放

void

autoReleaseTest2()

{

@autoreleasepool {

__autoreleasing TestClass *obj;

{

obj = [TestClass 

new

];

obj.name = @

"obj"

;

obj = nil;

NSLog(@

"把自動釋放對象在自動釋放池裡置空,其所指記憶體空間是不會被釋放的!"

);

}

NSLog(@

"出上面的大括号,隻要不出自動釋放池是不釋放所指記憶體空間的!"

);

}

}

        運作結果:

2014-08-13 20:06:45.890 ARCDemo[4448:303] 把自動釋放對象在自動釋放池裡置空,其所指記憶體空間是不會被釋放的!

2014-08-13 20:06:45.892 ARCDemo[4448:303] 出上面的大括号,隻要不出自動釋放池是不釋放所指記憶體空間的!

2014-08-13 20:06:45.892 ARCDemo[4448:303] obj,對象被釋放啦!

        代碼說明:由運作結果可以看出即使把指向記憶體空間的自動釋放類型的指針置空,其對應的記憶體空間不像強引用那樣被直接釋放掉,而是等到自動釋放池結束後在釋放,這就是延遲釋放。

    3.被自動釋放類型的指針用過的記憶體空間,在自動釋放池結束的時候一樣會被釋放掉。

//情況3:自動釋放類型和自動釋放池的配合,延遲對象釋放有可能造成暫時性的記憶體洩露

void

autoReleaseTest3()

{

@autoreleasepool {

__autoreleasing TestClass *obj;

{

obj = [TestClass 

new

];

obj.name = @

"firstObj"

;

NSLog(@

"上面的記憶體空間會由于下面的操作造成暫時記憶體洩露"

);

obj = [TestClass 

new

];

obj.name = @

"secondObj"

;

}

}

NSLog(@

"一塊釋放了兩個,上面配置設定的記憶體空間被自動釋放類型的變量用過,出自動釋放池時就會被釋放"

);

}

       代碼運作結果:

2014-08-13 20:12:37.512 ARCDemo[4459:303] 上面的記憶體空間會由于下面的操作造成暫時記憶體洩露

2014-08-13 20:12:37.514 ARCDemo[4459:303] secondObj,對象被釋放啦!

2014-08-13 20:12:37.514 ARCDemo[4459:303] firstObj,對象被釋放啦!

2014-08-13 20:12:37.515 ARCDemo[4459:303] 一塊釋放了兩個,上面配置設定的記憶體空間被自動釋放類型的變量用過,出自動釋放池時就會被釋放

        代碼說明:上面的代碼可能會引起記憶體洩露,因為如果第一次配置設定空間的時候如果我們往對象裡加入的是一個視訊,那麼在第二次給自動釋放類型的指針配置設定記憶體的時候,前面的記憶體空間不會被釋放掉,直到自動釋放池結束後兩個記憶體空間才會被釋放掉。

    四,strong, autoreleasing,weak混在一起的使用情況

        在weak中的例子,我們能得到weak和strong同指向一塊記憶體空間,當strong的指針不指向該記憶體空間時,這塊記憶體空間就可以被釋放掉,而weak指針被置零。

        記憶體空間隻要有autoreleasing或者strong的指針所持有,就不會被釋放

        1.strong和autoreleasing的混用

            (1).strong類型的指針指向自動釋放的空間

                  代碼如下:

18

void

strongVsAutorelease1()

{

{

//定義強引用對象的指針

TestClass *obj;

@autoreleasepool

{

//定義自動釋放類型的對象

__autoreleasing TestClass *obj1 = [TestClass 

new

];

obj1.name = @

"obj1"

;

//強引用對象的指針指向自動釋放類型對象的記憶體空間

obj = obj1;

}

NSLog(@

"自動釋放類型的對象記憶體空間不會被釋放,因為有strong類型的指針指向他"

);

}

NSLog(@

"出上面的大括号,強類型的指針被釋放,其指向的記憶體位址也會被釋放"

);

}

            運作結果如下:

2014-08-13 20:31:27.592 ARCDemo[4537:303] 自動釋放類型的對象記憶體空間不會被釋放,因為有strong類型的指針指向他

2014-08-13 20:31:38.895 ARCDemo[4537:303] obj1,對象被釋放啦!

2014-08-13 20:33:04.873 ARCDemo[4537:303] 出上面的大括号,強類型的指針被釋放,其指向的記憶體位址也會被釋放

            運作結果說明:上面是先讓自動釋放類型的指針指向該記憶體空間,然後再使強類型的指針指向該記憶體空間,在出自動釋放池的時候是不會釋放該記憶體空間的,直到強引用指針被釋放掉,才釋放該記憶體空間。

        (2).自動釋放類型的指針指向strong類型的指針所配置設定的空間的情況

void

strongVsAutorelease2()

{

@autoreleasepool {

//定義自動釋放類型的對象指針

__autoreleasing TestClass *obj;

{

//定義強引用類型的對象并配置設定記憶體

TestClass *obj1 = [TestClass 

new

];

obj1.name = @

"obj1"

;

//自動釋放類型的對象指針指向強引用類型記憶體空間

obj = obj1;

}

NSLog(@

"出上面的大括号,強引用類型指針指向的記憶體空間不會被釋放,因為為還有指針指向改記憶體"

);

}

NSLog(@

"堆配置設定的記憶體在出自動釋放池的時候被釋放了"

);

}

2014-08-13 20:47:55.259 ARCDemo[4591:303] 出上面的大括号,強引用類型指針指向的記憶體空間不會被釋放,以為還有指針指向改記憶體

2014-08-13 20:47:55.261 ARCDemo[4591:303] obj1,對象被釋放啦!

2014-08-13 20:47:55.261 ARCDemo[4591:303] 堆配置設定的記憶體在出自動釋放池的時候被釋放了

        結果說明:當strong修飾的指針随着棧的釋放而釋放,但其指向的記憶體空間并沒有被釋放,因為他還被自動釋放類型的指針所持有,是以在出自動釋放池的時候才會被釋放。

    (3).strong 類型的指針會指向自動釋放類型的空間記憶體,當strong指針被置空時該記憶體不會被釋放。

    ​    ​代碼如下:

19

//strong 類型的指針會指向自動釋放類型的空間記憶體,當strong指針被置空時該記憶體不會被釋放。

void

strongVsAutorelease3()

{

@autoreleasepool {

//定義自動釋放類型的對象指針并配置設定記憶體

__autoreleasing TestClass *obj = [TestClass 

new

];

obj.name = @

"obj"

;

{

//定義強引用類型的對象

__strong TestClass *obj1 = obj;

//自動釋放類型的對象指針指向強引用類型記憶體空間

obj1 = nil;

}

NSLog(@

"當obj1值空是其指向的記憶體空間不會被釋放"

);

}

NSLog(@

"當出四棟釋放池的時候,該記憶體空間會被釋放"

);

}

    ​代碼運作結果:

2014-08-14 09:08:33.311 ARCDemo[569:303] 當obj1值空是其指向的記憶體空間不會被釋放

2014-08-14 09:08:33.313 ARCDemo[569:303] obj,對象被釋放啦!

2014-08-14 09:08:33.313 ARCDemo[569:303] 當出四棟釋放池的時候,該記憶體空間會被釋放

    2.弱類型和自動釋放類型的混用

20

//weak類型的指針指向自動釋放的空間

void

 weak

Vsutorelease4()

{

{

//定義弱引用對象的指針

__weak TestClass *obj;

@autoreleasepool

{

//定義自動釋放類型的對象

__autoreleasing TestClass *obj1 = [TestClass 

new

];

obj1.name = @

"obj1"

;

//弱引用對象的指針指向自動釋放類型對象的記憶體空間

obj = obj1;

}

NSLog(@

"自動釋放類型的對象記憶體空間會被釋放,因為隻有weak類型的指針指向他"

);

NSLog(@

"%p"

, obj);

}

NSLog(@

"出上面的大括号,指針已經被釋放。"

);

}

2014-08-13 21:00:58.855 ARCDemo[4618:303] obj1,對象被釋放啦!

2014-08-13 21:00:58.857 ARCDemo[4618:303] 自動釋放類型的對象記憶體空間會被釋放,因為隻有weak類型的指針指向他

2014-08-13 21:00:58.857 ARCDemo[4618:303] 0x0

2014-08-13 21:00:58.858 ARCDemo[4618:303] 出上面的大括号,指針已經被釋放。

        代碼說明:即使有弱引用類型的指針指向該記憶體空間在出自動釋放池的時候,該記憶體空間也會被釋放。弱引用的指針會被置零。    

        上面寫了這麼多來點總結性的東西吧:strong 修飾的指針指向的空間如果沒有其他指針就會被釋放掉(weak類型的不算), 自動釋放類型的指針如果沒有其他類型的指針指向該記憶體空間時,當自動釋放池結束後就會釋放。

        上面的總結暫且這麼說吧,是根據筆者自己的了解所總結的内容,不免有偏頗之處,歡迎批評指正,轉載請注明出處。

作者:青玉伏案

出處:http://www.cnblogs.com/ludashi/

本文版權歸作者和共部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接配接,否則保留追究法律責任的權利。

如果文中有什麼錯誤,歡迎指出。以免更多的人被誤導。

收履歷:某網際網路公司,招聘iOS/Android靠譜工程師,入職後,可内部聯系樓主,有小禮品贈送,有意者可郵箱投遞履歷:[email protected]