IOS開發之對於Retain和Assign屬性的理解

來源:互聯網
上載者:User

原文地址:http://rd.189works.com/article-36809-1.html

下面本文:

在寫程式時,對於要定義的變數進了習慣於加上retain屬性,但對其到底起到什麼作用卻一直不是很明白,今天做了一個demo,終於弄清了他們的意思。

比如我要定義一下字串var:

NSString *var;

聲明Property時,@property(nonamtic,assign)NSString *var;

將屬性聲明為Assign,當使用@Synthsize產生Getter和Setter方法後,我們就可以用“.”操作符來為var賦值,

將屬性聲明為Assign時,Setter方法的實現是這樣的:

-(void)setter:(NSString*)str

{

   var=str;

}

var=[[NSString alloc]initWithString:@"aaa"];

當我們這樣用時,沒有調用Setter方法,只是將var指向目標地址,這時

NSLog(@“%d”,[var retainCount]);的值會是1。

如果我們這樣寫:self.var=[[NSString alloc]initWithString:@"aaa"];

這時會調用setter方法,但是NSLog(@“%d”,[var retainCount]);的值仍然是1。

如果我們在設定屬性時這樣來設定:

@property(nonamtic,retain)NSString *var;

這時,自動產生的Setter方法是這樣的:

-(void)Setter:(NSString*)str

  [str retain];

  [var release];

  var=str;

這樣就一目瞭然了,也就是被設定為retain屬性的方法,在產生Setter方法時,先將要賦的值的引用計數加1,然後將var指向的對象release。再後才是將str賦值給var。這時下面的代碼的輸出結果會是2,因為,在Setteryyif中[[NSString alloc]initWithString:@"aaa"]所指的記憶體地區的retain被加了1,然後var又指向這個地區,所以var的retainCount自然就為2了:

self.var=[[NSString alloc]initWithString:@"aaa"];

NSLog(@“%d”,[var retainCount]);

這樣做是為了防止記憶體被過渡釋放,比如,[[NSString alloc]initWithString:@"aaa"]這個記憶體地區不光有var指向,還有另外一個變數var2也指向這個地區,如果我們不設定成retain屬性,在執行var=[[NSString alloc]initWithString:@"aaa"];後,var和var1都指向一個共同的記憶體地區,但這個記憶體地區的retainCount為1。如果在另一個地方執行了[var2 release];這樣[[NSString alloc]initWithString:@"aaa"]所指的記憶體地區的retainCount為0,所以這個記憶體地區就被釋放了,var也就成了野指標,這時再引用var將會出現記憶體錯誤。

但是,需要特別注意的是,即使我們設定var的屬性為retain,如果我們在為var賦值時,使用的是如下形式:

var=[[NSString alloc]initWithString:@"aaa"];

而不是:

self.var=[[NSString alloc]initWithString:@"aaa"];

這樣retain屬性是不起作用的,前面不加self.,相當於我們定義的屬性還是assign。retainCount不會加1的。

我個人對這個的理解是,如果不顯示的使用"."操作符,Setter方法是不調用的,僅僅是指標的傳遞。

今天又有了點新的想法,補充一下:

"."操作符在OBJC中是方法的調用,比如:self.str和[self str]是一樣的。如果我在.h檔案中聲明了一個方法:

-(void)method;

那麼,我調用這個方法可以用兩種方式:[self method]或self.method。

這樣說的話,我們為什麼可以用諸如self.str這樣的形式來表示一個變數呢,原因就在於OBJC中變數屬性的機制。

我們前面說過,定義一個變數str,加個assign或retain之類的屬性後,再用@synthesize就可以產生相應的setter和getter方法了。這樣,對於一個變數,就有了相應的賦值方法,於是,對於self.str這樣的寫法,實際上就是調用了str對應的setter或getter方法。換句話說,也是把setter或getter訊息發送給和str。str這時就是一個方法名,而不僅僅是變數名了。

所以如果我們沒有對一個變數聲明屬性,也沒有@synthesize來產生setter和getter方法,那麼我們就不能用self.str這種形式,而只能用str=@"aaa",或者str1=str這樣的形式來使用變數。

於是也就有了我們前面的結論:用self.str這種形式,相當於調用setter或getter方法。也就會執行retain的操作。不用這種形式,就沒用調用setter或getter方法,retain的操作也就無從談起。

 

轉一個另一篇文章,講的比較好:

最近有人問我關於什麼時候用self.賦值的問題, 我總結了一下, 發出來給大家參考. 有什麼問題請大家斧正.

    關於什麼時間用self. , 其實是和Obj-c的存取方法有關, 不過網上很多人也都這麼解答的, 那它為什麼和存取方法有關? 怎麼有關的? 並沒有多少人回答出來. 同時關於記憶體管理的內容, 請大家看旺財勇士的Objective-C記憶體管理總結~CC專版 , 有些東西我就不多解釋了.

進入正題, 我們經常會在官方文檔裡看到這樣的代碼:

 

MyClass.h

 

@interface MyClass : NSObject {
    MyObject *myObject;
}
@property (nonatomic, retain) MyObject *myObject;
@end

 

MyClass.m

 

 
@synthesize myObject;
 
-(id)init{
    if(self = [super init]){
        MyObject * aMyObject = [[MyObject alloc] init];
        self.myObject = aMyObject;
        [aMyObject release];
    }
    return self;
}

 


有人就問, 為什麼要這麼複雜的賦值? 為什麼要加self. ? 直接寫成self.myObject = [[MyObject alloc] init];不是也沒有錯麼? 不加self有時好像也是正常的?

 

現在我們來看看記憶體管理的內容:

 

先看間接賦值的:

 

1.加self.

 

 MyObject * aMyObject = [[MyObject alloc] init]; //aMyObject retainCount = 1;
 self.myObject = aMyObject; //myObject retainCount = 2;
 [aMyObject release];//myObject retainCount = 1;

 

2. 不加self.

 

MyObject * aMyObject = [[MyObject alloc] init]; //aMyObject retainCount = 1;
myObject = aMyObject; //myObject retainCount = 1;
[aMyObject release];//對象己經被釋放

 

再看直接賦值的:

 

3.加self.

 

self.myObject = [[MyObject alloc] init]; //myObject retainCount = 2;

 

4. 不加self.

 

myObject = [[MyObject alloc] init]; //myObject retainCount = 1;

 

現在是不是有點暈, 我們先來把代碼改一下, 官方的一種常見寫法:

 

MyClass.h

 

@interface MyClass : NSObject {
    MyObject * _myObject;
}
@property (nonatomic, retain) MyObject *myObject;
@end

 

MyClass.m

@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方法是:

 

-(MyObject*)myObject{
    return _myObject;
}

 

Set方法是:

 

// assign 
-(void)setMyObject:(id)newValue{
    _myObject = newValue; 
}
// retain 
-(void)setMyObject:(id)newValue{
    if (_myObject != newValue) { 
        [_myObject release]; 
        _myObject = [newValue retain]; 
    }  
}
// copy 
-(void)setMyObject:(id)newValue{
    if (_myObject != newValue) { 
        [_myObject release]; 
        _myObject = [newValue copy]; 
    } 
}

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.