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是這么一回事。好啦,廢話少說,用代碼講東西才是王道,代碼走起。(為了方便我們觀察內存的釋放情況,可以設置斷點來單步運行)
為了做測試使用,我們建一個測試類,並重寫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
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類型的不算), 自動釋放類型的指針如果沒有其他類型的指針指向該內存空間時,當自動釋放池結束后就會釋放。
上面的總結暫且這么說吧,是根據筆者自己的理解所總結的內容,不免有偏頗之處,歡迎批評指正,轉載請注明出處。