IOS之Objective-C學習筆記(六)


在之前的一片文章我們說了OC中謂詞操作:http://blog.csdn.net/jiangwei0910410003/article/details/41923507,從今天開始我們就來看一下OC中最難的一部分內容:內存管理

為什么說他難呢?因為內存如果需要我們程序員去管理的話,那個難度肯定是很大的,如果是Java,垃圾回收器會把這份工作給做了,我們不需要關心,但是就是因為如此,Android運行速度上會慢一下,原因很簡單,Java的垃圾回收器有很多收集算法的,這個在回收的過程中是很浪費時間的,效率自然就低了,但是如果這份工作給程序員自己去做的話,效率上肯定會增加,但是對於程序員來說任務就比較繁重了,而且還要特別的小心,千萬不能造成內存溢出和泄露。

這里我們主要從四個方面來介紹一下內存管理

1、簡單的例子來了解引用計數的使用

2、set方法來控制引用計數問題

3、銷毀方法來控制引用計數問題

4、初始化方法來控制引用計數問題


下面就來簡單看一下OC中的內存管理

這個例子有點復雜,我們慢慢分析

Dog.h

[objc]  view plain copy 在CODE上查看代碼片 派生到我的代碼片
  1. //  
  2. //  Dog.h  
  3. //  24_MemeryManager  
  4. //  
  5. //  Created by jiangwei on 14-10-12.  
  6. //  Copyright (c) 2014年 jiangwei. All rights reserved.  
  7. //  
  8.   
  9. #import <Foundation/Foundation.h>  
  10.   
  11. @interface Dog : NSObject{  
  12.     NSString *_name;  
  13. }  
  14.   
  15. - (void) setName:(NSString *)name;  
  16.   
  17. @end  

Dog.m

[objc]  view plain copy 在CODE上查看代碼片 派生到我的代碼片
  1. //  
  2. //  Dog.m  
  3. //  24_MemeryManager  
  4. //  
  5. //  Created by jiangwei on 14-10-12.  
  6. //  Copyright (c) 2014年 jiangwei. All rights reserved.  
  7. //  
  8.   
  9. #import "Dog.h"  
  10.   
  11. @implementation Dog  
  12.   
  13. - (void) setName:(NSString *)name{  
  14.     _name = name;  
  15. }  
  16.   
  17. @end  

Dog類中定義了name屬性,並且給他提供了一個set方法


Person.h

[objc]  view plain copy 在CODE上查看代碼片 派生到我的代碼片
  1. //  
  2. //  Person.h  
  3. //  24_MemeryManager  
  4. //  
  5. //  Created by jiangwei on 14-10-12.  
  6. //  Copyright (c) 2014年 jiangwei. All rights reserved.  
  7. //  
  8.   
  9. #import <Foundation/Foundation.h>  
  10.   
  11. @class Dog;  
  12. @interface Person : NSObject{  
  13.     Dog *_dog;  
  14.     NSString * _name;  
  15. }  
  16.   
  17. - (id)initWithDog:(Dog*)dog;  
  18. - (void)setName:(NSString *)name;  
  19. - (void)setDog:(Dog *)dog;  
  20. - (void)playDog;  
  21. - (Dog *)dog;  
  22.   
  23. - (void)dealloc;  
  24. @end  


Person.m

[objc]  view plain copy 在CODE上查看代碼片 派生到我的代碼片
  1. //  
  2. //  Person.m  
  3. //  24_MemeryManager  
  4. //  
  5. //  Created by jiangwei on 14-10-12.  
  6. //  Copyright (c) 2014年 jiangwei. All rights reserved.  
  7. //  
  8.   
  9. #import "Person.h"  
  10.   
  11. @implementation Person  
  12.   
  13. - (id)initWithDog:(Dog*)dog{  
  14.     //使用初始化的時候傳入dog  
  15.     self = [super init];  
  16.     if(self != nil){  
  17.         //因為初始化方法只會調用一次,所以這里就沒有做判斷了  
  18.         [_dog release];  
  19.         _dog = [dog retain];  
  20.     }  
  21.     return self;  
  22. }  
  23.   
  24. - (void)setName:(NSString *)name{  
  25.     //這里name也是對象,所以也是需要進行改寫  
  26.     _name = name;  
  27.       
  28.     /* 
  29.      //這里的判斷是因為setName方法可能會被多次調用 
  30.      if(_name != name){ 
  31.         [_name release]; 
  32.         [name copy];//這里使用了copy,而沒有使用retain,這個是字符串獨有的,其他對象類型都是使用retain的 
  33.      } 
  34.      */  
  35. }  
  36.   
  37. //第一種方式  
  38. /* 
  39. - (void)setDog:(Dog *)dog{ 
  40.     //引用計數需要+1 
  41.     _dog = [dog retain]; 
  42.      
  43.     //有時候可能需要替換Dog對象,所以這里還要注意釋放Dog的引用 
  44. } 
  45.  */  
  46.   
  47. //第二種方式  
  48. /* 
  49. - (void)setDog:(Dog *)dog{ 
  50.     //使用nil去調用方法是沒有錯誤的 
  51.     //但是當一個對象被銷毀的時候,指針就變成野指針了,這時候調用方法會出錯的 
  52.     [_dog release]; 
  53.     _dog = [dog retain]; 
  54. } 
  55.  */  
  56.   
  57. //第三種方式  
  58. - (void)setDog:(Dog *)dog{  
  59.     //這里的判斷是因為setName方法可能會被多次調用  
  60.     if(_dog != dog){  
  61.         [_dog release];  
  62.         _dog = [dog retain];  
  63.     }  
  64. }  
  65.   
  66. - (void)playDog{  
  67.     NSLog(@"playDog");  
  68. }  
  69.   
  70. - (Dog *)dog{  
  71.     return _dog;  
  72. }  
  73.   
  74. - (void)dealloc{  
  75.     //對象類型的屬性都需要在這里進行釋放引用  
  76.     //對狗進行釋放  
  77.     [_dog release];  
  78.     NSLog(@"dealloc is Executing");  
  79.     [super dealloc];  
  80. }  
  81.   
  82. @end  
Person類中有一個Dog的屬性,然后提供了set方法。代碼有點復雜,我們后面會詳細說明

下面來看一下測試代碼

main.m

[objc]  view plain copy 在CODE上查看代碼片 派生到我的代碼片
  1. //  
  2. //  main.m  
  3. //  24_MemeryManager  
  4. //  
  5. //  Created by jiangwei on 14-10-12.  
  6. //  Copyright (c) 2014年 jiangwei. All rights reserved.  
  7. //  
  8.   
  9. #import <Foundation/Foundation.h>  
  10.   
  11. #import "Person.h"  
  12. #import "Dog.h"  
  13.   
  14. //內存管理  
  15. //alloc用來創建對象,創建完成之后,引用計數為1,只調用一次  
  16. //retain使引用計數+1,可以多調用幾次  
  17. //release使引用計數-1,可以多調用幾次  
  18. //當引用計數為0的時候會調用dealloc  
  19. //最新的Xcode版本默認情況下會開啟ARC機制的,當開啟這個機制之后,我們就不能手動的顯示調用這些方法,編譯器會報錯  
  20. //所以我們可以將這個默認狀態的ARC關閉,但是這個只是為了測試使用  
  21.   
  22. int main(int argc, const charchar * argv[]) {  
  23.   
  24.     /* 
  25.     Person *person = [[Person alloc] init];//引用計數為1 
  26.     NSLog(@"引用計數:%ld",[person retainCount]); 
  27.      
  28.     //引用計數加1 
  29.     [person retain]; 
  30.      
  31.     [person release]; 
  32.      
  33.     NSLog(@"引用計數:%ld",[person retainCount]); 
  34.     [person release]; 
  35.      */  
  36.       
  37.       
  38.     Dog *dog = [[Dog alloc] init];  
  39.     [dog setName:@"小黑"];  
  40.       
  41.     Dog *dog1 = [[Dog alloc] init];//引用計數為1  
  42.     [dog setName:@"大黃"];  
  43.       
  44.     Person *p1 = [[Person alloc] init];  
  45.     [p1 setName:@"張三"];  
  46.     [p1 setDog:dog];  
  47.     [p1 setDog:dog1];//狗的引用替換成了大黃  
  48.       
  49.     Person *p2 = [[Person alloc] init];  
  50.     [p2 setName:@"李四"];  
  51.     [p2 setDog:dog];  
  52.       
  53.     //這里引用計數為1,這個和我們之前說的引用計數管理有矛盾,所以我們在使用的時候需要手動的retain  
  54.     NSLog(@"引用計數為:%ld",[dog retainCount]);  
  55.       
  56.     [dog1 release];//因為alloc的時候引用計數就為1了  
  57.       
  58.     //這里就有一個問題了,dog1對象已經被銷毀了,但是setDog對象還是用了dog1對象調用方法了,這就會報錯了  
  59.     //所以又對set方法進行改進了  
  60.     [p1 setDog:dog1];  
  61.       
  62.     //當人銷毀的時候,還需要對狗的引用-1  
  63.     //在人的dealloc方法中實現  
  64.       
  65.     Person *p3 = [[Person alloc] initWithDog:dog1];  
  66.     [dog1 release];//dog1的引用計數:0  
  67.       
  68.     [p3 playDog];  
  69.       
  70.     [p3 release];  
  71.       
  72.     return 0;  
  73. }  


下面我們來詳細說明一下:

首先如果想演示這個例子的話,需要修改一下設置:



最新的XCode默認是會自動選上ARC(Automatic Reference Counting),如果我們不把這個手動關閉的話,代碼中會報錯的。

alloc用來創建對象,創建完成之后,引用計數為1,只調用一次

retain使引用計數+1,可以多調用幾次

release使引用計數-1,可以多調用幾次

當引用計數為0的時候會調用dealloc

黃金法則:每次調用alloc一次,都需要調用release一次,他們兩是成對出現的

最新的Xcode版本默認情況下會開啟ARC機制的,當開啟這個機制之后,我們就不能手動的顯示調用這些方法,編譯器會報錯

所以我們可以將這個默認狀態的ARC關閉,但是這個只是為了測試使用


同時我們會發現main.m文件中沒有自動釋放池了@autoreleasepool了。


1、簡單的例子

[objc]  view plain copy 在CODE上查看代碼片 派生到我的代碼片
  1. Person *person = [[Person alloc] init];//引用計數為1  
  2. NSLog(@"引用計數:%ld",[person retainCount]);  
  3.   
  4. //引用計數加1  
  5. [person retain];  
  6.   
  7. [person release];  
  8.   
  9. NSLog(@"引用計數:%ld",[person retainCount]);  
  10. [person release];  
我們創建了一個Person類,然后可以打印一下的引用計數值:1

當我們調用retain方法的時候,引用計數就會+1,當我們調用release方法的時候引用計數會-1

一旦系統發現引用計數為0的時候,就會銷毀這個對象,調用dealloc方法


2、set方法來控制引用計數

這個例子簡單吧,沒什么問題的,現在我們把問題復雜化

[java]  view plain copy 在CODE上查看代碼片 派生到我的代碼片
  1. Dog *dog = [[Dog alloc] init];  
  2. [dog setName:@"小黑"];  
  3.   
  4. Dog *dog1 = [[Dog alloc] init];//引用計數為1  
  5. [dog setName:@"大黃"];  
  6.   
  7. Person *p1 = [[Person alloc] init];  
  8. [p1 setName:@"張三"];  
  9. [p1 setDog:dog];  
  10. [p1 setDog:dog1];//狗的引用替換成了大黃  
  11.   
  12. Person *p2 = [[Person alloc] init];  
  13. [p2 setName:@"李四"];  
  14. [p2 setDog:dog];  
  15.   
  16. //這里引用計數為1,這個和我們之前說的引用計數管理有矛盾,所以我們在使用的時候需要手動的retain  
  17. NSLog(@"引用計數為:%ld",[dog retainCount]);  
  18.   
  19. [dog1 release];//因為alloc的時候引用計數就為1了  
  20.   
  21. //這里就有一個問題了,dog1對象已經被銷毀了,但是setDog對象還是用了dog1對象調用方法了,這就會報錯了  
  22. //所以又對set方法進行改進了  
  23. [p1 setDog:dog1];  
  24.   
  25. //當人銷毀的時候,還需要對狗的引用-1  
  26. //在人的dealloc方法中實現  
我們定義了兩條狗,然后將這兩條狗通過setDog方法設置到p1對象上,同時將第一條狗設置到p2對象上,這時候我們打印一下第一條狗的引用計數,發現他的引用計數是1,原因很簡單,這個1是在alloc時候有的,但是現在是有問題的,因為第一條狗被p1和p2引用者,按照正常情況,第一條狗的引用計數為3的。所以這時候我們就需要修改一下Person類中setDog方法了

[objc]  view plain copy 在CODE上查看代碼片 派生到我的代碼片
  1. - (void)setDog:(Dog *)dog{  
  2.     //引用計數需要+1  
  3.     _dog = [dog retain];  
  4.       
  5.     //有時候可能需要替換Dog對象,所以這里還要注意釋放Dog的引用  
  6. }  
我們需要手動的增加dog的引用計數,這樣就正常了。


現在又有一個問題了,上面p1對象設置了第二條狗

[objc]  view plain copy 在CODE上查看代碼片 派生到我的代碼片
  1. [p1 setDog:dog1];//狗的引用替換成了大黃  
按照上面的setDog方法,我們又發現一個問題,現在p1引用的狗是第二條狗了,不是第一條,那么這時候按照正常情況第一條狗的引用計數-1,因為他已經不再被p1引用了。所以setDog方法還得修改:

[objc]  view plain copy 在CODE上查看代碼片 派生到我的代碼片
  1. - (void)setDog:(Dog *)dog{  
  2.     //使用nil去調用方法是沒有錯誤的  
  3.     //但是當一個對象被銷毀的時候,指針就變成野指針了,這時候調用方法會出錯的  
  4.     [_dog release];  
  5.     _dog = [dog retain];  
  6. }  
這時候我們就在賦值之前,對_dog調用一次release方法,這樣就解決了上面的那個問題。


但是現在還有一個問題:

[java]  view plain copy 在CODE上查看代碼片 派生到我的代碼片
  1. [dog1 release];//因為alloc的時候引用計數就為1了  
  2. NSLog(@"dog1:%ld",[dog1 retainCount]);  
  3. //這里就有一個問題了,dog1對象已經被銷毀了,但是setDog對象還是用了dog1對象調用方法了,這就會報錯了  
  4. //所以又對set方法進行改進了  
  5. [p1 setDog:dog1];  
在執行release代碼之后,dog1的引用計數為1,這是繼續調用setDog方法:

[objc]  view plain copy 在CODE上查看代碼片 派生到我的代碼片
  1. - (void)setDog:(Dog *)dog{  
  2.     //使用nil去調用方法是沒有錯誤的  
  3.     //但是當一個對象被銷毀的時候,指針就變成野指針了,這時候調用方法會出錯的  
  4.     [_dog release];  
  5.     _dog = [dog retain];  
  6. }  
這時候_dog是dog1,繼續調用release方法,執行完之后,引用繼續為0了,對象被釋放了,但是這時候dog參數的值還是dog1,那么在執行retain方法就會報錯了,這個原因很簡單,就是我們兩次調用了setDog方法,將dog1設置了兩次。中間調用一次release方法。所以解決這樣的問題,我們在修改一下setDog方法:

[objc]  view plain copy 在CODE上查看代碼片 派生到我的代碼片
  1. //第三種方式  
  2. - (void)setDog:(Dog *)dog{  
  3.     //這里的判斷是因為setName方法可能會被多次調用  
  4.     if(_dog != dog){  
  5.         [_dog release];  
  6.         _dog = [dog retain];  
  7.     }  
  8. }  
我只需要判斷一下,之前的屬性值和當前需要設置的值是否相同。這種方式就是沒有問題了,所以我們以后在寫set方法的時候,這就是模板了。


3、銷毀方法dealloc控制引用計數

現在假如p1被銷毀了,那么對於dog1來說引用計數應該-1的,所以需要在Person類中的dealloc方法中修改一下:

[objc]  view plain copy 在CODE上查看代碼片 派生到我的代碼片
  1. - (void)dealloc{  
  2.     //對象類型的屬性都需要在這里進行釋放引用  
  3.     //對狗進行釋放  
  4.     [_dog release];  
  5.     NSLog(@"dealloc is Executing");  
  6.     [super dealloc];  
  7. }  


4、初始化方法控制引用計數

現在還有一種情況,如果我們使用初始化方法來設置Dog屬性值:

[objc]  view plain copy 在CODE上查看代碼片 派生到我的代碼片
  1. - (id)initWithDog:(Dog*)dog{  
  2.     //使用初始化的時候傳入dog  
  3.     self = [super init];  
  4.     if(self != nil){  
  5.         //因為初始化方法只會調用一次,所以這里就沒有做判斷了  
  6.         [_dog release];  
  7.         _dog = [dog retain];  
  8.     }  
  9.     return self;  
  10. }  
我們這里的處理和setDog方法的處理方式一樣,但是這里不需要做一次判斷了,因為初始化方法只會調用一次。所以不會出現setDog的第三種情況。





注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
  © 2014-2022 ITdaan.com