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 |
#import @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 weakVsutorelease4() { { //定義弱引用物件的指標 __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型別的不算), 自動釋放型別的指標如果沒有其他型別的指標指向該記憶體空間時,當自動釋放池結束後就會釋放。
上面的總結暫且這麼說吧,是根據筆者自己的理解所總結的內容,不免有偏頗之處,歡迎批評指正,轉載請註明出處。
打賞支援我寫出更多好文章,謝謝!
打賞作者
打賞支援我寫出更多好文章,謝謝!