ARC在OC裏面我的感受又是一個高大上的牛詞,在前面Objective-C中的內存管理部分提到了ARC內存管理機制,ARC是Automatic Reference Counting---自動引用計數。有自動引用計數,那麼就得有手動引用計數MRC(Mannul Reference Counting),前面已經提到過了MRC。那麼在ARC模式下是否是意味着咱們就能夠一點也不用進行內存管理的呢?並非這樣的,咱們還須要代碼進行內存的管理。下面會結合着代碼把OC中的ARC機制作一個詳細的總結(歡迎你們批評指針,轉載請註明出處 )。安全
在ARC機制下是少不了下面這些東西的:測試
1.關鍵字 __strong 默認值,表示只要有強引用指針指向該變量,則該變量會一直存在。atom
2.關鍵字__weak 弱引用,表示若沒有任何強引用指針指向該變量,會自動將變量的值置爲空,即nil狀態。spa
3.關鍵字 __autoreleasing 用於標示自動釋放的變量指針
4.__unsafe_unretained 不安全的弱引用,若沒有任何強引用指針指向該變量,不會自動設爲空,會成爲野指針。code
關於Weak和Strong,看下圖吧:視頻
第一次接觸ARC的小夥伴們看到上面的概念可能會一頭霧水,上面說的是個啥?都是哪跟哪?不用着急,下面會有實例代碼,結合着實例代碼,而後再作一個總結,你就會有種豁然開朗的感受。你就會明白,哦,原來ARC是這麼一回事。好啦,廢話少說,用代碼講東西纔是王道,代碼走起。(爲了方便咱們觀察內存的釋放狀況,能夠設置斷點來單步運行)對象
爲了作測試使用,咱們建一個測試類,並重寫dealloc方法來觀察內存的釋放狀況,測試類以下;blog
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
2
3
4
5
6
7
8
9
10
11
|
//狀況1.當指向內存的指針在出棧時,內存被釋放
void
strongTest1()
{
//測試用的代碼塊
{
//默認爲強引用的變量
TestClass *obj1 = [TestClass
new
];
obj1.name = @
"obj1"
;
}
NSLog(@
"在出上面的大括號時,指針變量被釋放,堆分配的內存也會別當即釋放"
);
}
|
代碼運行結果:
1
2
|
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.給對象指針重寫分配內存的狀況,代碼以下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
//狀況2.當對象指針 指向其餘內存時,原有的內存若沒有指針指向他,就會被當即釋放
void
strongTest2()
{
{
TestClass * obj1 = [TestClass
new
];
obj1.name = @
"obj1"
;
//給已經分配內存的指針在分配新的內存
obj1 = [TestClass
new
];
NSLog(@
"通過上面一步會釋放第一次分配的堆內存!"
);
obj1.name = @
"obj1_new"
;
}
NSLog(@
"出大括號回釋放第二次分配的內存"
);
}
|
代碼運行結果:
1
2
3
4
|
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.把對象指針置爲空時,分配的堆內存會當即被釋放掉。相應的代碼以下:
1
2
3
4
5
6
7
8
9
10
|
void
strongTest3()
{
{
TestClass * obj = [TestClass
new
];
obj.name = @
"obj"
;
obj = nil;
NSLog(@
"把指針置空時,指針指向的內存空間會被釋放"
);
}
}
|
代碼運行結果:
1
2
|
2014-08-13 19:42:34.827 ARCDemo[4373:303] obj,對象被釋放啦!
2014-08-13 19:42:34.829 ARCDemo[4373:303] 把指針置空時,指針指向的內存空間會被釋放
|
代碼說明:把指向該內存空間的對象指針置空,就至關於沒有指針指向該內存空間,因此在strong下會被當即釋放。
4.把新的對象指針指向堆內存空間,而後把原有的指針進行置空
代碼以下:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
//狀況4.把新的對象指針指向堆內存空間,而後把原有的指針進行置空
void
strongTest4()
{
{
TestClass * obj1 = [TestClass
new
];
obj1.name = @
"obj1"
;
TestClass * obj2 = obj1;
obj1 = nil;
NSLog(@
"obj1指向的內存不會被釋放,由於還有obj2指向"
);
}
}
|
運行結果:
1
2
|
2014-08-13 19:46:06.554 ARCDemo[4394:303] obj1指向的內存不會被釋放,由於還有obj2指向
2014-08-13 19:46:06.556 ARCDemo[4394:303] obj1,對象被釋放啦!
|
代碼說明:當兩個指針同時指向一塊內存空間時,把原有的指針置爲空,這塊內存空間不會被釋放的,由於還有其餘的指針指向該內存空間。
二. __weak 歸零弱引用:在若指針指向的內存被釋放後,若引用的指針則會置零
歸零弱引用:弱引用的指針指向強引用的內存時,是不影響其釋放內存空間的,當弱引用指針所指空間被釋放掉得時候,該弱引用指針會被置零。
代碼以下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
//weak: 歸零弱引用:在若指針指向的內存被釋放後,若引用的指針則會置零
void
weakTest()
{
//定義弱引用指針
__weak TestClass *obj1;
{
//默認爲強引用
TestClass *obj2 = [TestClass
new
];
obj2.name = @
"obj2"
;
//弱引用指針指向obj2
obj1 = obj2;
NSLog(@
"強制引用堆分配得內存空間被釋放前obj1的地址爲:%p"
, obj1);
}
NSLog(@
"強制引用堆分配得內存空間被釋放後obj1的地址爲:%p"
, obj1);
}
|
運行結果以下:
1
2
3
|
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
//狀況1:自動釋放類型和自動釋放池配合,提早釋放對象,會產生野指針
void
autoReleaseTest1()
{
//定義自動釋放對象指針
__autoreleasing TestClass *obj;
//定義釋放池
@autoreleasepool {
obj = [TestClass
new
];
obj.name = @
"obj"
;
}
//此時obj爲野指針
NSLog(@
"obj_p = %p"
,obj);
}
|
代碼運行結果:
1
2
|
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結合着自動釋放池會延遲內存空間的釋放
代碼以下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
//狀況2.自動釋放類型和自動釋放池配合,延遲對象的釋放
void
autoReleaseTest2()
{
@autoreleasepool {
__autoreleasing TestClass *obj;
{
obj = [TestClass
new
];
obj.name = @
"obj"
;
obj = nil;
NSLog(@
"把自動釋放對象在自動釋放池裏置空,其所指內存空間是不會被釋放的!"
);
}
NSLog(@
"出上面的大括號,只要不出自動釋放池是不釋放所指內存空間的!"
);
}
}
|
運行結果:
1
2
3
|
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.被自動釋放類型的指針用過的內存空間,在自動釋放池結束的時候同樣會被釋放掉。
代碼以下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
//狀況3:自動釋放類型和自動釋放池的配合,延遲對象釋放有可能形成暫時性的內存泄露
void
autoReleaseTest3()
{
@autoreleasepool {
__autoreleasing TestClass *obj;
{
obj = [TestClass
new
];
obj.name = @
"firstObj"
;
NSLog(@
"上面的內存空間會因爲下面的操做形成暫時內存泄露"
);
obj = [TestClass
new
];
obj.name = @
"secondObj"
;
}
}
NSLog(@
"一塊釋放了兩個,上面分配的內存空間被自動釋放類型的變量用過,出自動釋放池時就會被釋放"
);
}
|
代碼運行結果:
1
2
3
4
|
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類型的指針指向自動釋放的空間
代碼以下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
void
strongVsAutorelease1()
{
{
//定義強引用對象的指針
TestClass *obj;
@autoreleasepool
{
//定義自動釋放類型的對象
__autoreleasing TestClass *obj1 = [TestClass
new
];
obj1.name = @
"obj1"
;
//強引用對象的指針指向自動釋放類型對象的內存空間
obj = obj1;
}
NSLog(@
"自動釋放類型的對象內存空間不會被釋放,由於有strong類型的指針指向他"
);
}
NSLog(@
"出上面的大括號,強類型的指針被釋放,其指向的內存地址也會被釋放"
);
}
|
運行結果以下:
1
2
3
|
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類型的指針所分配的空間的狀況
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
void
strongVsAutorelease2()
{
@autoreleasepool {
//定義自動釋放類型的對象指針
__autoreleasing TestClass *obj;
{
//定義強引用類型的對象並分配內存
TestClass *obj1 = [TestClass
new
];
obj1.name = @
"obj1"
;
//自動釋放類型的對象指針指向強引用類型內存空間
obj = obj1;
}
NSLog(@
"出上面的大括號,強引用類型指針指向的內存空間不會被釋放,由於爲還有指針指向改內存"
);
}
NSLog(@
"堆分配的內存在出自動釋放池的時候被釋放了"
);
}
|
代碼運行結果:
1
2
3
|
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指針被置空時該內存不會被釋放。
代碼以下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
//strong 類型的指針會指向自動釋放類型的空間內存,當strong指針被置空時該內存不會被釋放。
void
strongVsAutorelease3()
{
@autoreleasepool {
//定義自動釋放類型的對象指針並分配內存
__autoreleasing TestClass *obj = [TestClass
new
];
obj.name = @
"obj"
;
{
//定義強引用類型的對象
__strong TestClass *obj1 = obj;
//自動釋放類型的對象指針指向強引用類型內存空間
obj1 = nil;
}
NSLog(@
"當obj1值空是其指向的內存空間不會被釋放"
);
}
NSLog(@
"當出四棟釋放池的時候,該內存空間會被釋放"
);
}
|
代碼運行結果:
1
2
3
|
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.弱類型和自動釋放類型的混用
代碼以下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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(@
"出上面的大括號,指針已經被釋放。"
);
}
|
代碼運行結果:
1
2
3
4
|
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類型的不算), 自動釋放類型的指針若是沒有其餘類型的指針指向該內存空間時,當自動釋放池結束後就會釋放。
上面的總結暫且這麼說吧,是根據筆者本身的理解所總結的內容,難免有偏頗之處,歡迎批評指正,轉載請註明出處。