ObjectiveC的類變量賦值的內存泄漏分析

  
  
  
  
     1. MyClass.h
     2.    
     3. @interface MyClass : NSObject {
     4.         MyObject *myObject;
     5. }
     6. @property (nonatomic, retain) MyObject *myObject;
     7. @end
     8.    
     9. MyClass.m
    10.    
    11.    
    12. @synthesize myObject;
    13.    
    14. -(id)init{
    15.         if(self = [super init]){
    16.                 MyObject * aMyObject = [[MyObject alloc] init];
    17.                 self.myObject = aMyObject;
    18.                 [aMyObject release];
    19.         }
    20.         return self;
    21. }

  建立一個Student類,繼承NSObject類,代碼:
  
  
  
  
     1. #import <Foundation/Foundation.h>
     2.    
     3. @interface Student : NSObject{
     4.    
     5.             NSString *idd;
     6.             NSString *name;
     7.     }
     8.     @property (nonatomic, retain) NSString *idd;
     9.     @property (nonatomic, retain) NSString *name;
    10.        
    11.     @end


.m文件 代碼:
  
  
  
  
  1. #import "Student.h" 
  2.    
  3.   @implementation Student 
  4.   @synthesize idd,name; 
  5.     
  6.   - (void)dealloc 
  7.   { 
  8.       [idd release]; 
  9.       [name release]; 
  10.      [super dealloc]; 
  11.   } 
  12.     
  13.    
  14.   @end 
使用@propety @synthesize實現Student的成員屬性的set get方法。一般咱們在其餘類裏訪問Student的成員屬性的作法:
獲取student的名字經過student.name,給名字賦值[student setName:@「jordy」]; 其中student是Student類對象,若是在Student類內部訪問其成員屬性使用[self setName:@」jordy」], 訪問使用self.name;
注意:上述的代碼,因爲wordpress的緣由,代碼中的字符會自動保存爲中文格式。你在使用時記得改成英文格式。
在Student.h和Student.m文件,是咱們習慣性的寫法,但彷佛仍是不能解釋什麼加self和不加self的區別,請看下面代碼,是另外一種習慣性的寫法,還以Student類爲例:
.h文件 代碼:
  
  
  
  
  1. #import <Foundation/Foundation.h> 
  2. @interface Student : NSObject{ 
  3.   
  4.     NSString *_idd; 
  5.    NSString *_name; 
  6. @property (nonatomic, retain) NSString *idd; 
  7. @property (nonatomic, retain) NSString *name; 
  8. @end 
.m文件 代碼:
  
  
  
  
  1. #import "Student.h" 
  2.   
  3. @implementation Student 
  4.  @synthesize idd = _idd; 
  5. @synthesize name = _name; 
  6.   
  7.  - (void)dealloc 
  8.  { 
  9.     [_idd release]; 
  10.     _idd = nil; 
  11.      [_name release]; 
  12.     _name = nil; 
  13.      [super dealloc]; 
  14.  } 
  15. @end 
能夠注意到上述代碼,與以前的代碼,在.h文件name變量改寫爲了_name;在.m文件中@sythesize的寫法也發生了變化。
若是經過方法self._name獲取屬性的值,xcode編譯器會提示錯誤,其實這也就說明了,咱們一般使用self.name實際使用的是student類name的get方法,同理name的set方法亦是如此。
有人就問, 爲何要這麼複雜的賦值? 爲何要加self. ? 直接寫成self.myObject = [[MyObject alloc] init];不是也沒有錯麼? 不加self有時好像也是正常的?
接下來從內存管理來講明使用self.和不使用self的區別
如今咱們來看看內存管理的內容:
ViewController.h文件,使用Student類,代碼以下:
  
  
  
  
  1. #import <UIKit/UIKit.h> 
  2.  @class Student; 
  3.   
  4.  @interface ViewController : UIViewController{ 
  5.       
  6.     Student *_student; 
  7.  } 
  8.      
  9.   @property (nonatomic, retain) Student *student; 
  10.     
  11.  @end 

ViewController.m文件,代碼:
  
  
  
  
  1. #import "ViewController.h" 
  2.  #import "Student.h" 
  3.     
  4.   @implementation ViewController 
  5.   @synthesize student = _student; 
  6.     
  7.   - (void)didReceiveMemoryWarning 
  8.   { 
  9.       [super didReceiveMemoryWarning]; 
  10.   } 
  11.     
  12.   #pragma mark - View lifecycle 
  13.     
  14.   - (void)viewDidLoad 
  15.   { 
  16.       [super viewDidLoad]; 
  17.   } 
  18.     
  19.   - (void) dealloc 
  20.   { 
  21.         [_student release]; 
  22.         _student = nil; 
  23.      [super dealloc]; 
  24.     } 

其它的方法沒有使用到,因此這裏就不在顯示了。
在ViewController.m的viewDidLoad方法中建立一個Student類的對象
  
  
  
  
  1. Student *mystudent = [[Student alloc] init]; 
  2.  self.student = mystudent; 
  3.  [mystudent release]; 
這是相信有人會有疑問了,問什麼建立student對象要這麼複雜,彷佛直接使用self.student = [[Student alloc] init]; 也沒有問題,不加self有時也是挺正常的呀?
接下來就須要從內存角度來分析它們之間的區別了:
先看間接賦值的:
1.加self.
  
  
  
  
  1. MyObject * aMyObject = [[MyObject alloc] init]; //aMyObject retainCount = 1; 
  2. self.myObject = aMyObject; //myObject retainCount = 2; 
  3. [aMyObject release];//myObject retainCount = 1; 
2. 不加self.
  
  
  
  
  1. MyObject * aMyObject = [[MyObject alloc] init]; //aMyObject retainCount = 1; 
  2. myObject = aMyObject; //myObject retainCount = 1; 
  3. [aMyObject release];//對象己經被釋放 
再看直接賦值的:
3.加self.
  
  
  
  
  1. self.myObject = [[MyObject alloc] init]; //myObject retainCount = 2; 
4. 不加self.
  
  
  
  
  1. myObject = [[MyObject alloc] init]; //myObject retainCount = 1; 
如今是否是有點暈, 咱們先來把代碼改一下, 官方的一種常見寫法:
  
  
  
  
  1. MyClass.h 
  2. @interface MyClass : NSObject { 
  3.     MyObject * _myObject; 
  4. @property (nonatomic, retain) MyObject *myObject; 
  5. @end 
  6. MyClass.m 
  7. @synthesize myObject = _myObject; 
OK, 你如今再試下, 若是你用self._myObject = aMyObject; 或者 myObject = aMyObject; 你會獲得一個錯誤, 爲何呢, 這裏就是和Obj-c的存取方法有關了. 說白了很簡單 , 你們都知道, @property (nonatomic, retain) MyObject *myObject; 是爲一個屬性設置存取方法, 只是平時咱們用的方法名和屬性名是同樣的,如今你把它寫成不一樣的名字, 就會很清楚了. _myObject是屬性自己, myObject是存取方法名.
如今咱們知道self.是訪問屬性的存取方法了, 那存取方法又怎麼工做的? self.myObject = [[MyObject alloc] init]; 爲何會有內存泄露?
關於nonatomic我很少解釋了, 它不是我要講的重點, 並且我也沒徹底搞清楚, 不誤導你們. 我只說assign, retain ,copy.
get方法是:
  
  
  
  
  1. -(MyObject*)myObject{ 
  2.     return _myObject; 
Set方法是:
  
  
  
  
  1. // assign 
  2. -(void)setMyObject:(id)newValue{ 
  3.     _myObject = newValue; 
  4. // retain 
  5. -(void)setMyObject:(id)newValue{ 
  6.     if (_myObject != newValue) { 
  7.         [_myObject release]; 
  8.         _myObject = [newValue retain]; 
  9.     }  
  10. // copy 
  11. -(void)setMyObject:(id)newValue{ 
  12.     if (_myObject != newValue) { 
  13.         [_myObject release]; 
  14.         _myObject = [newValue copy]; 
  15.     } 
其實這些方法裏還有別的內容, 並不僅是這些. 並且這些方法能夠被重寫. 好比你寫一個
  
  
  
  
  1. -(MyObject*)myObject{ 
  2.     return _myObject; 
放在你的類裏, 你調用self.myObject時(不要把它放在等號左邊, 那會調用get方法)就會調用這個方法.
這裏多說一句, @property 是爲你設置存取方法, 和你的屬性無關, 你能夠只寫一句
  
  
  
  
  1. @property (readonly) NSString *name; 
在你的類裏實現
  
  
  
  
  1. -(NSString*)name{ 
  2.     NSLog(@"name"); 
  3.     return @"MyClass"
一樣能夠用self.name調用.
如今回頭說說咱們開始的那四個賦值, 當不用self.的時候,  那句話只是通常的賦值, 把一個指針賦給另外一個指針, 不會對分配的內存有任何影響, 因此2中不要最後[aMyObject release];這句話和4是一回事. 這裏就很少說了.咱們看看1和3,
當調用setMyObject:方法時, 對newValue 作了一次retain操做, 咱們必須把原來的newValue釋放掉, 否則就會內存泄露, 在1裏, 咱們有個aMyObject能夠用來釋放, 在3裏, 咱們沒法釋放它, 因此, 在3裏, 咱們會多出來一個retainCount. 內存泄露了.
說了這麼多, 我只想讓你們清楚, 什麼是調用屬性自己, 什麼是調用存取方法. 怎麼樣才能避免內存泄露, 並且, 以上例子裏是在本身類裏的調用, 若是這個類被別的類調用時, 更要注意一些,
順便說一下, 若是你想在其它類訪問對象屬性, 而不是經過存取方法, 你能夠用myClass -> myObject來訪問, 這樣是直接訪問對象自己, 不過你先要把myObject設成@public. 但這個是官方不提倡的,
相關文章
相關標籤/搜索