ARC内存管理机制详解
ARC在OC里面?zhèn)€人感覺又是一個(gè)高大上的牛詞,在前面Objective-C中的內(nèi)存管理部分提到了ARC內(nèi)存管理機(jī)制,ARC是Automatic Reference Counting—自動(dòng)引用計(jì)數(shù)。有自動(dòng)引用計(jì)數(shù),那么就得有手動(dòng)引用計(jì)數(shù)MRC(Mannul Reference Counting),前面已經(jīng)提到過了MRC。那么在ARC模式下是不是意味著我們就可以一點(diǎn)也不用進(jìn)行內(nèi)存管理的呢?并不是這樣的,我們還需要代碼進(jìn)行內(nèi)存的管理。下面會(huì)結(jié)合著代碼把OC中的ARC機(jī)制做一個(gè)詳細(xì)的總結(jié).
在ARC機(jī)制下是少不了下面這些東西的:
1.關(guān)鍵字 __strong 默認(rèn)值,表示只要有強(qiáng)引用指針指向該變量,則該變量會(huì)一直存在。
?
2.關(guān)鍵字__weak 弱引用,表示若沒有任何強(qiáng)引用指針指向該變量,會(huì)自動(dòng)將變量的值置為空,即nil狀態(tài)。
?
3.關(guān)鍵字 __autoreleasing 用于標(biāo)示自動(dòng)釋放的變量
?
4.__unsafe_unretained 不安全的弱引用,若沒有任何強(qiáng)引用指針指向該變量,不會(huì)自動(dòng)設(shè)為空,會(huì)成為野指針。
關(guān)于Weak和Strong,看下圖吧:
第一次接觸ARC的小伙伴們看到上面的概念可能會(huì)一頭霧水,上面說的是個(gè)啥?都是哪跟哪?不用著急,下面會(huì)有實(shí)例代碼,結(jié)合著實(shí)例代碼,然后再做一個(gè)總結(jié),你就會(huì)有種豁然開朗的感覺。你就會(huì)明白,哦,原來ARC是這么一回事。好啦,廢話少說,用代碼講東西才是王道,代碼走起。(為了方便我們觀察內(nèi)存的釋放情況,可以設(shè)置斷點(diǎn)來單步運(yùn)行)
?
為了做測(cè)試使用,我們建一個(gè)測(cè)試類,并重寫dealloc方法來觀察內(nèi)存的釋放情況,測(cè)試類如下;
?
#import
@interface TestClass : NSObject
@property(nonatomic, strong) NSString *name;
@end
#import "TestClass.h"
@implementation TestClass
//dealloc在對(duì)象釋放是回調(diào)用
-(void)dealloc
{
NSLog(@"%@,對(duì)象被釋放啦!", _name);
}
@end
?
一.__strong: 強(qiáng)引用,是ARC中變量聲明的默認(rèn)值
?
用大白話講就是你手動(dòng)分配的堆內(nèi)存,如果沒有指針指向這塊內(nèi)存,那么這塊內(nèi)存就會(huì)被回收
?
1.當(dāng)聲明變量為強(qiáng)引用時(shí),對(duì)象的指針出棧時(shí),如果該指針指向的內(nèi)存空間沒有別的指針指向他,就自動(dòng)掉用dealloc方法釋放堆內(nèi)存測(cè)試代碼如下:
?
//情況1.當(dāng)指向內(nèi)存的指針在出棧時(shí),內(nèi)存被釋放
void strongTest1()
{
//測(cè)試用的代碼塊
{
//默認(rèn)為強(qiáng)引用的變量
TestClass *obj1 = [TestClass new];
obj1.name = @"obj1";
}
NSLog(@"在出上面的大括號(hào)時(shí),指針變量被釋放,堆分配的內(nèi)存也會(huì)別立即釋放");
}
代碼運(yùn)行結(jié)果:
2015-12-27?19:25:52.378 ARCDemo[4345:303] obj1,對(duì)象被釋放啦!
2015-12-27?19:25:52.380 ARCDemo[4345:303] 在出上面的大括號(hào)時(shí),指針變量被釋放,堆分配的內(nèi)存也會(huì)別立即釋放
?
代碼說明:從運(yùn)行結(jié)果來看,出代碼塊后我們定于的指針變量會(huì)隨著我們代碼塊的結(jié)束而釋放,就沒有指針指向我們分配的堆內(nèi)存了,以為默認(rèn)為strong,所以在ARC機(jī)制下會(huì)立即調(diào)用dealloc來釋放堆內(nèi)存。
?
2.給對(duì)象指針重寫分配內(nèi)存的情況,代碼如下:
?
//情況2.當(dāng)對(duì)象指針 指向其他內(nèi)存時(shí),原有的內(nèi)存若沒有指針指向他,就會(huì)被立即釋放
void strongTest2()
{
{
TestClass * obj1 = [TestClass new];
obj1.name = @"obj1";
?
//給已經(jīng)分配內(nèi)存的指針在分配新的內(nèi)存
obj1 = [TestClass new];
NSLog(@"經(jīng)過上面一步會(huì)釋放第一次分配的堆內(nèi)存!");
?
obj1.name = @"obj1_new";
}
NSLog(@"出大括號(hào)回釋放第二次分配的內(nèi)存");
?
}
2015-12-27?19:30:38.455 ARCDemo[4356:303] obj1,對(duì)象被釋放啦!
2015-12-27?19:30:38.456 ARCDemo[4356:303] 經(jīng)過上面一步會(huì)釋放第一次分配的堆內(nèi)存!
2015-12-27?19:30:38.457 ARCDemo[4356:303] obj1_new,對(duì)象被釋放啦!
2015-12-27?19:30:38.457 ARCDemo[4356:303] 出大括號(hào)回釋放第二次分配的內(nèi)存
?
代碼說明:我們先給strong類型的對(duì)象指針分配內(nèi)存空間,然后再次分配內(nèi)存空間,在第二次分配空間的時(shí)候,就沒有對(duì)象指針指向原有的內(nèi)存空間,所以在第二次分配空間之后就會(huì)把原有的內(nèi)存空間給釋放掉,在出代碼塊的時(shí)候,對(duì)象指針也會(huì)隨著棧內(nèi)存的釋放而釋放掉,也沒有對(duì)象指針指向第二次分配的內(nèi)存了,所以會(huì)被釋放掉。
?
3.把對(duì)象指針置為空時(shí),分配的堆內(nèi)存會(huì)立即被釋放掉。相應(yīng)的代碼如下:
?
void strongTest3()
{
{
TestClass * obj = [TestClass new];
obj.name = @"obj";
?
obj = nil;
NSLog(@"把指針置空時(shí),指針指向的內(nèi)存空間會(huì)被釋放");
}
}
?
代碼運(yùn)行結(jié)果:
?
2015-12-27?19:42:34.827?ARCDemo[4373:303]?obj,對(duì)象被釋放啦!
2015-12-27?19:42:34.829?ARCDemo[4373:303]?把指針置空時(shí),指針指向的內(nèi)存空間會(huì)被釋放
| ? |
代碼說明:把指向該內(nèi)存空間的對(duì)象指針置空,就相當(dāng)于沒有指針指向該內(nèi)存空間,所以在strong下會(huì)被立即釋放。
?
4.把新的對(duì)象指針指向堆內(nèi)存空間,然后把原有的指針進(jìn)行置空
?
代碼如下:
?
//情況4.把新的對(duì)象指針指向堆內(nèi)存空間,然后把原有的指針進(jìn)行置空
void strongTest4()
{
{
TestClass * obj1 = [TestClass new];
obj1.name = @"obj1";
?
TestClass * obj2 = obj1;
obj1 = nil;
NSLog(@"obj1指向的內(nèi)存不會(huì)被釋放,因?yàn)檫€有obj2指向");
?
}
}
?
運(yùn)行結(jié)果:
?
2015-12-27?19:46:06.554 ARCDemo[4394:303] obj1指向的內(nèi)存不會(huì)被釋放,因?yàn)檫€有obj2指向
2015-12-27?19:46:06.556 ARCDemo[4394:303] obj1,對(duì)象被釋放啦!
?
代碼說明:當(dāng)兩個(gè)指針同時(shí)指向一塊內(nèi)存空間時(shí),把原有的指針置為空,這塊內(nèi)存空間不會(huì)被釋放的,因?yàn)檫€有其他的指針指向該內(nèi)存空間。
?
二. __weak 歸零弱引用:在若指針指向的內(nèi)存被釋放后,若引用的指針則會(huì)置零
?
歸零弱引用:弱引用的指針指向強(qiáng)引用的內(nèi)存時(shí),是不影響其釋放內(nèi)存空間的,當(dāng)弱引用指針?biāo)缚臻g被釋放掉得時(shí)候,該弱引用指針會(huì)被置零。
?
代碼如下:
?
//weak: 歸零弱引用:在若指針指向的內(nèi)存被釋放后,若引用的指針則會(huì)置零
void weakTest()
{
//定義弱引用指針
__weak TestClass *obj1;
{
//默認(rèn)為強(qiáng)引用
TestClass *obj2 = [TestClass new];
obj2.name = @"obj2";
//弱引用指針指向obj2
obj1 = obj2;
NSLog(@"強(qiáng)制引用堆分配得內(nèi)存空間被釋放前obj1的地址為:%p", obj1);
}
NSLog(@"強(qiáng)制引用堆分配得內(nèi)存空間被釋放后obj1的地址為:%p", obj1);
}
?
運(yùn)行結(jié)果如下:
?
2015-12-27?19:55:31.393 ARCDemo[4413:303] 強(qiáng)制引用堆分配得內(nèi)存空間被釋放前obj1的地址為:0x100201ea0
2015-12-27?19:55:31.395 ARCDemo[4413:303] obj2,對(duì)象被釋放啦!
2015-12-27?19:55:31.395 ARCDemo[4413:303] 強(qiáng)制引用堆分配得內(nèi)存空間被釋放后obj1的地址為:0x0
?
代碼說明:當(dāng)出大括號(hào)時(shí)強(qiáng)引用指針會(huì)被釋放掉,之前開辟的堆內(nèi)存空間只有一個(gè)弱引用指針指向他,所以在ARC中會(huì)被自動(dòng)釋放,弱引用指針會(huì)置零。
?
三. __autoreleasing 自動(dòng)釋放,一般結(jié)合著@autoreleasepool使用。
?
1.自動(dòng)釋放修飾的指針?biāo)赶虻膬?nèi)存空間會(huì)在自動(dòng)釋放池結(jié)束的時(shí)候會(huì)被釋放,代碼如下
?
//情況1:自動(dòng)釋放類型和自動(dòng)釋放池配合,提前釋放對(duì)象,會(huì)產(chǎn)生野指針
void autoReleaseTest1()
{
?
//定義自動(dòng)釋放對(duì)象指針
__autoreleasing TestClass *obj;
//定義釋放池
@autoreleasepool {
obj = [TestClass new];
obj.name = @"obj";
}
//此時(shí)obj為野指針
NSLog(@"obj_p = %p",obj);
?
}
?
代碼運(yùn)行結(jié)果:
?
2015-12-27?20:02:00.489 ARCDemo[4436:303] obj,對(duì)象被釋放啦!
2015-12-27?20:02:00.490 ARCDemo[4436:303] obj_p = 0x100108f00
?
代碼說明:自動(dòng)釋放池結(jié)束后,自動(dòng)對(duì)象指針指向的內(nèi)存空間會(huì)被釋放,但上面的用法會(huì)產(chǎn)生野指針。
?
2.__autoreleasing結(jié)合著自動(dòng)釋放池會(huì)延遲內(nèi)存空間的釋放
?
代碼如下:
?
//情況2.自動(dòng)釋放類型和自動(dòng)釋放池配合,延遲對(duì)象的釋放
void autoReleaseTest2()
{
@autoreleasepool {
__autoreleasing TestClass *obj;
{
obj = [TestClass new];
obj.name = @"obj";
obj = nil;
NSLog(@"把自動(dòng)釋放對(duì)象在自動(dòng)釋放池里置空,其所指內(nèi)存空間是不會(huì)被釋放的!");
}
NSLog(@"出上面的大括號(hào),只要不出自動(dòng)釋放池是不釋放所指內(nèi)存空間的!");
}
}
?
運(yùn)行結(jié)果:
?
2015-12-27?20:06:45.890 ARCDemo[4448:303] 把自動(dòng)釋放對(duì)象在自動(dòng)釋放池里置空,其所指內(nèi)存空間是不會(huì)被釋放的!
2015-12-27?20:06:45.892 ARCDemo[4448:303] 出上面的大括號(hào),只要不出自動(dòng)釋放池是不釋放所指內(nèi)存空間的!
2015-12-27?20:06:45.892 ARCDemo[4448:303] obj,對(duì)象被釋放啦!
?
代碼說明:由運(yùn)行結(jié)果可以看出即使把指向內(nèi)存空間的自動(dòng)釋放類型的指針置空,其對(duì)應(yīng)的內(nèi)存空間不像強(qiáng)引用那樣被直接釋放掉,而是等到自動(dòng)釋放池結(jié)束后在釋放,這就是延遲釋放。
?
3.被自動(dòng)釋放類型的指針用過的內(nèi)存空間,在自動(dòng)釋放池結(jié)束的時(shí)候一樣會(huì)被釋放掉。
?
代碼如下:
?
//情況3:自動(dòng)釋放類型和自動(dòng)釋放池的配合,延遲對(duì)象釋放有可能造成暫時(shí)性的內(nèi)存泄露
void autoReleaseTest3()
{
@autoreleasepool {
__autoreleasing TestClass *obj;
{
obj = [TestClass new];
obj.name = @"firstObj";
?
NSLog(@"上面的內(nèi)存空間會(huì)由于下面的操作造成暫時(shí)內(nèi)存泄露");
?
obj = [TestClass new];
obj.name = @"secondObj";
}
}
NSLog(@"一塊釋放了兩個(gè),上面分配的內(nèi)存空間被自動(dòng)釋放類型的變量用過,出自動(dòng)釋放池時(shí)就會(huì)被釋放");
}
?
?
代碼運(yùn)行結(jié)果:
?
2015-12-27?20:12:37.512 ARCDemo[4459:303] 上面的內(nèi)存空間會(huì)由于下面的操作造成暫時(shí)內(nèi)存泄露
2015-12-27?20:12:37.514 ARCDemo[4459:303] secondObj,對(duì)象被釋放啦!
2015-12-27?20:12:37.514 ARCDemo[4459:303] firstObj,對(duì)象被釋放啦!
2015-12-27?20:12:37.515 ARCDemo[4459:303] 一塊釋放了兩個(gè),上面分配的內(nèi)存空間被自動(dòng)釋放類型的變量用過,出自動(dòng)釋放池時(shí)就會(huì)被釋放
?
代碼說明:上面的代碼可能會(huì)引起內(nèi)存泄露,因?yàn)槿绻谝淮畏峙淇臻g的時(shí)候如果我們往對(duì)象里加入的是一個(gè)視頻,那么在第二次給自動(dòng)釋放類型的指針分配內(nèi)存的時(shí)候,前面的內(nèi)存空間不會(huì)被釋放掉,直到自動(dòng)釋放池結(jié)束后兩個(gè)內(nèi)存空間才會(huì)被釋放掉。
?
四,strong, autoreleasing,weak混在一起的使用情況
?
在weak中的例子,我們能得到weak和strong同指向一塊內(nèi)存空間,當(dāng)strong的指針不指向該內(nèi)存空間時(shí),這塊內(nèi)存空間就可以被釋放掉,而weak指針被置零。
?
內(nèi)存空間只要有autoreleasing或者strong的指針?biāo)钟?#xff0c;就不會(huì)被釋放
?
1.strong和autoreleasing的混用
?
(1).strong類型的指針指向自動(dòng)釋放的空間
?
代碼如下:
?
void strongVsAutorelease1()
{
{
//定義強(qiáng)引用對(duì)象的指針
TestClass *obj;
@autoreleasepool
{
//定義自動(dòng)釋放類型的對(duì)象
__autoreleasing TestClass *obj1 = [TestClass new];
obj1.name = @"obj1";
?
//強(qiáng)引用對(duì)象的指針指向自動(dòng)釋放類型對(duì)象的內(nèi)存空間
obj = obj1;
}
NSLog(@"自動(dòng)釋放類型的對(duì)象內(nèi)存空間不會(huì)被釋放,因?yàn)橛衧trong類型的指針指向他");
}
NSLog(@"出上面的大括號(hào),強(qiáng)類型的指針被釋放,其指向的內(nèi)存地址也會(huì)被釋放");
}
?
運(yùn)行結(jié)果如下:
?
2015-12-27?20:31:27.592 ARCDemo[4537:303] 自動(dòng)釋放類型的對(duì)象內(nèi)存空間不會(huì)被釋放,因?yàn)橛?span id="ze8trgl8bvbq" class="crayon-m">strong類型的指針指向他
2015-12-27?20:31:38.895 ARCDemo[4537:303] obj1,對(duì)象被釋放啦!
2015-12-27?20:33:04.873 ARCDemo[4537:303] 出上面的大括號(hào),強(qiáng)類型的指針被釋放,其指向的內(nèi)存地址也會(huì)被釋放
?
運(yùn)行結(jié)果說明:上面是先讓自動(dòng)釋放類型的指針指向該內(nèi)存空間,然后再使強(qiáng)類型的指針指向該內(nèi)存空間,在出自動(dòng)釋放池的時(shí)候是不會(huì)釋放該內(nèi)存空間的,直到強(qiáng)引用指針被釋放掉,才釋放該內(nèi)存空間。
?
(2).自動(dòng)釋放類型的指針指向strong類型的指針?biāo)峙涞目臻g的情況
?
void strongVsAutorelease2()
{
@autoreleasepool {
//定義自動(dòng)釋放類型的對(duì)象指針
__autoreleasing TestClass *obj;
{
//定義強(qiáng)引用類型的對(duì)象并分配內(nèi)存
TestClass *obj1 = [TestClass new];
obj1.name = @"obj1";
//自動(dòng)釋放類型的對(duì)象指針指向強(qiáng)引用類型內(nèi)存空間
obj = obj1;
}
NSLog(@"出上面的大括號(hào),強(qiáng)引用類型指針指向的內(nèi)存空間不會(huì)被釋放,因?yàn)闉檫€有指針指向改內(nèi)存");
}
NSLog(@"堆分配的內(nèi)存在出自動(dòng)釋放池的時(shí)候被釋放了");
}
?
代碼運(yùn)行結(jié)果:
?
代碼運(yùn)行結(jié)果:
2015-12-27?20:47:55.259 ARCDemo[4591:303] 出上面的大括號(hào),強(qiáng)引用類型指針指向的內(nèi)存空間不會(huì)被釋放,以為還有指針指向改內(nèi)存
2015-12-27?20:47:55.261 ARCDemo[4591:303] obj1,對(duì)象被釋放啦!
2015-12-27?20:47:55.261 ARCDemo[4591:303] 堆分配的內(nèi)存在出自動(dòng)釋放池的時(shí)候被釋放了
?
結(jié)果說明:當(dāng)strong修飾的指針隨著棧的釋放而釋放,但其指向的內(nèi)存空間并沒有被釋放,因?yàn)樗€被自動(dòng)釋放類型的指針?biāo)钟?#xff0c;所以在出自動(dòng)釋放池的時(shí)候才會(huì)被釋放。
?
(3).strong 類型的指針會(huì)指向自動(dòng)釋放類型的空間內(nèi)存,當(dāng)strong指針被置空時(shí)該內(nèi)存不會(huì)被釋放。
?
代碼如下:
?
//strong 類型的指針會(huì)指向自動(dòng)釋放類型的空間內(nèi)存,當(dāng)strong指針被置空時(shí)該內(nèi)存不會(huì)被釋放。
void strongVsAutorelease3()
{
@autoreleasepool {
//定義自動(dòng)釋放類型的對(duì)象指針并分配內(nèi)存
__autoreleasing TestClass *obj = [TestClass new];
obj.name = @"obj";
?
{
//定義強(qiáng)引用類型的對(duì)象
__strong TestClass *obj1 = obj;
//自動(dòng)釋放類型的對(duì)象指針指向強(qiáng)引用類型內(nèi)存空間
obj1 = nil;
}
?
NSLog(@"當(dāng)obj1值空是其指向的內(nèi)存空間不會(huì)被釋放");
}
NSLog(@"當(dāng)出四棟釋放池的時(shí)候,該內(nèi)存空間會(huì)被釋放");
}
?
代碼運(yùn)行結(jié)果:
?
2015-12-27?09:08:33.311 ARCDemo[569:303] 當(dāng)obj1值空是其指向的內(nèi)存空間不會(huì)被釋放
2015-12-27?09:08:33.313 ARCDemo[569:303] obj,對(duì)象被釋放啦!
2015-12-27?09:08:33.313 ARCDemo[569:303] 當(dāng)出四棟釋放池的時(shí)候,該內(nèi)存空間會(huì)被釋放
?
2.弱類型和自動(dòng)釋放類型的混用
?
代碼如下:
?
//weak類型的指針指向自動(dòng)釋放的空間
void weakVsutorelease4()
{
{
//定義弱引用對(duì)象的指針
__weak TestClass *obj;
@autoreleasepool
{
//定義自動(dòng)釋放類型的對(duì)象
__autoreleasing TestClass *obj1 = [TestClass new];
obj1.name = @"obj1";
?
//弱引用對(duì)象的指針指向自動(dòng)釋放類型對(duì)象的內(nèi)存空間
obj = obj1;
}
NSLog(@"自動(dòng)釋放類型的對(duì)象內(nèi)存空間會(huì)被釋放,因?yàn)橹挥衱eak類型的指針指向他");
NSLog(@"%p", obj);
}
NSLog(@"出上面的大括號(hào),指針已經(jīng)被釋放。");
}
?
代碼運(yùn)行結(jié)果:
?
2015-12-27?21:00:58.855 ARCDemo[4618:303] obj1,對(duì)象被釋放啦!
2015-12-27?21:00:58.857 ARCDemo[4618:303] 自動(dòng)釋放類型的對(duì)象內(nèi)存空間會(huì)被釋放,因?yàn)橹挥?span id="ze8trgl8bvbq" class="crayon-m">weak類型的指針指向他
2015-12-27?21:00:58.857 ARCDemo[4618:303] 0x0
2015-12-27?21:00:58.858 ARCDemo[4618:303] 出上面的大括號(hào),指針已經(jīng)被釋放。
?
代碼說明:即使有弱引用類型的指針指向該內(nèi)存空間在出自動(dòng)釋放池的時(shí)候,該內(nèi)存空間也會(huì)被釋放。弱引用的指針會(huì)被置零。
?
上面寫了這么多來點(diǎn)總結(jié)性的東西吧:strong 修飾的指針指向的空間如果沒有其他指針就會(huì)被釋放掉(weak類型的不算), 自動(dòng)釋放類型的指針如果沒有其他類型的指針指向該內(nèi)存空間時(shí),當(dāng)自動(dòng)釋放池結(jié)束后就會(huì)釋放。
轉(zhuǎn)載于:https://www.cnblogs.com/zhangz-1511/p/5096591.html
總結(jié)
以上是生活随笔為你收集整理的ARC内存管理机制详解的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android中Cursor类的概念和用
- 下一篇: ios基础篇(二十六)—— UITabl