原文地址: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];
}
}