以前不少網友對我翻譯的教程中的Property的使用感到有些疑惑不解,搞不清楚何時要release,何時要self.xxx = nil;同時對於Objective-c的內存管理以及cocos2d的內存管理規則不夠清楚。本文主要講解objc裏面@property,它是什麼,它有什麼用,atomic,nonatomic,readonly,readwrite,assign,retain,copy,getter,setter這些關鍵字有什麼用,何時使用它們。至於Objc的內存管理和cocos2d的內存管理部分,接下來,我會翻譯Ray的3篇教程,那裏面再和你們詳細討論。今天咱們的主要任務是搞定@property。
學過c/c++的朋友都知道,咱們定義struct/class的時候,若是把訪問限定符(public,protected,private)設置爲public的話,那麼咱們是能夠直接用.號來訪問它內部的數據成員的。好比
//in Test.h
class Test
{
public:
int i;
float f;
};
我在main函數裏面是能夠經過下面的方式來使用這個類的:(注意,若是在main函數裏面使用此類,除了要包含頭文件之外,最重要的是記得把main.m改爲main.mm,不然會報一些奇怪的錯誤。因此,任什麼時候候咱們使用c++,若是報奇怪的錯誤,那就要提醒本身是否是把相應的源文件改爲.mm後綴了。其它引用此類的文件有時候也要改爲.mm文件)
//in main.mm
Test test;
test.i =1;
test.f =2.4f;
NSLog(@ "Test.i = %d, Test.f = %f",test.i,? test.f);
可是,在objc裏面,咱們能不能這樣作呢?請看下面的代碼:(新建一個objc類,命名爲BaseClass)
//in BaseClass.h
@interface BaseClass : NSObject{
@public
NSString *_name;
}
接下來,咱們在main.mm裏面:
BaseClass *base= [[BaseClass alloc] init];
base.name? =@"set?base name";
NSLog(@ "base class's name = %@", base.name);
不用等你編譯,xcode4立刻提示錯誤,請看截圖:html
請你們注意看出錯提示「Property 'nam' not found on object of type BaseClass*",意思是,BaseClass這類沒有一個名爲name的屬性。即便咱們在頭文件中聲明瞭@public,咱們仍然沒法在使用BaseClass的時候用.號來直接訪問其數據成員。而@public,@protected和@private只會影響繼承它的類的訪問權限,若是你使用@private聲明數據成員,那麼在子類中是沒法直接使用父類的私有成員的,這和c++,java是同樣的。
既然有錯誤,那麼咱們就來想法解決啦,編譯器說沒有@property,那好,咱們就定義property,請看代碼:
//in BaseClass.h
@interface BaseClass : NSObject{
@public
NSString *_name;
}
@property(nonatomic,copy) NSString *name;
//in BaseClass.m
@synthesize name = _name;
如今,編譯並運行,ok,很好。那你可能會問了@prperty是否是就是讓」. "號合法了呀?只要定義了@property就可使用.號來訪問類的數據成員了?先讓咱們來看下面的例子:
@interface BaseClass : NSObject{
@public
NSString *_name;
}
//@property(nonatomic,copy) NSString *name;
-(NSString*) name;
-(void) setName:(NSString*)newName;
我把@property的定義註釋掉了,另外定義了兩個函數,name和setName,下面請看實現文件:
//@synthesize?name = _name;
-(NSString*) name{
return _name;
}
-(void) setName:(NSString *)name{
if (_name != name) {
[_name release];
_name = [name copy];
}
}java
如今,你再編譯運行,同樣工做的很好。why?由於我剛剛作的工做和先前聲明@property所作的工做徹底同樣。@prperty只不過是給編譯器看的一種指令,它能夠編譯以後爲你生成相應的getter和setter方法。並且,注意看到面property(nonatomic,copy)括號裏面這copy參數了嗎?它所作的事就是
_name = [name copy];
若是你指定retain,或者assign,那麼相應的代碼分別是:
//property(retain)NSString* name;
_name = [name retain];
//property(assign)NSString* name;
_name = name;
其它講到這裏,你們也能夠看出來,@property並不僅是能夠生成getter和setter方法,它還能夠作內存管理。不過這裏我暫不討論。如今,@property大概作了件什麼事,想必你們已經知道了。可是,咱們程序員都有一個坎,就是本身沒有徹底吃透的東西,內心用起來不踏實,特別是我本身。因此,接下來,咱們要詳細深挖@property的每個細節。
首先,咱們看atomic 與nonatomic的區別與用法,講以前,咱們先看下面這段代碼:
@property(nonatomic, retain) UITextField *userName;??? //1
@property(nonatomic, retain,readwrite) UITextField *userName;? //2
@property(atomic, retain) UITextField *userName;? //3
@property(retain) UITextField *userName;? //4
@property(atomic,assign) int i;???????? // 5
@property(atomic) int i;???????? //6
@property int i;?????????????? //7
請讀者先停下來想想,它們有什麼區別呢?
上面的代碼1和2是等價的,3和4是等價的,5,6,7是等價的。也就是說atomic是默認行爲,assign是默認行爲,readwrite是默認行爲。可是,若是你寫上@property(nontomic)NSString *name;那麼將會報一個警告,以下圖:c++
由於是非gc的對象,因此默認的assign修飾符是不行的。那麼何時用assign、何時用retain和copy呢?推薦作法是NSString用copy,delegate用assign(且必定要用assign,不要問爲何,只管去用就是了,之後你會明白的),非objc數據類型,好比int,float等基本數據類型用assign(默認就是assign),而其它objc類型,好比NSArray,NSDate用retain。
在繼續以前,我還想補充幾個問題,就是若是咱們本身定義某些變量的setter方法,可是想讓編譯器爲咱們生成getter方法,這樣子能夠嗎?答案是固然能夠。若是你本身在.m文件裏面實現了setter/getter方法的話,那以翻譯器就不會爲你再生成相應的getter/setter了。請看下面代碼:
//代碼一:
@interface BaseClass : NSObject{
@public
NSString *_name;
}
@property(nonatomic,copy,readonly) NSString *name;? //這裏使用的是readonly,全部會聲明geter方法
-(void) setName:(NSString*)newName;
//代碼二:
@interface BaseClass : NSObject{
@public
NSString *_name;
}
@property(nonatomic,copy,readonly) NSString *name;?? //這裏雖然聲明瞭readonly,可是不會生成getter方法,由於你下面本身定義了getter方法。
-(NSString*) name;?? //getter方法是否是隻能是name呢?不必定,你打開Foundation.framework,找到UIView.h,看看裏面的property就明白了)
-(void) setName:(NSString*)newName;
//代碼三:
@interface BaseClass : NSObject{
@public
NSString *_name;
}
@property(nonatomic,copy,readwrite) NSString *name;? //這裏編譯器會咱們生成了getter和setter
//代碼四:
@interface BaseClass : NSObject{
@public
NSString *_name;
}
@property(nonatomic,copy) NSString *name;? //由於readwrite是默認行爲,因此同代碼三
上面四段代碼是等價的,接下來,請看下面四段代碼:
//代碼一:
@synthesize name = _name;? //這句話,編譯器發現你沒有定義任何getter和setter,因此會同時會你生成getter和setter
//代碼二:
@synthesize name = _name;? //由於你定義了name,也就是getter方法,因此編譯器只會爲生成setter方法,也就是setName方法。
-(NSString*) name{
NSLog(@"name");
return _name;
}
//代碼三:
@synthesize name = _name;?? //這裏由於你定義了setter方法,因此編譯器只會爲你生成getter方法
-(void) setName:(NSString *)name{
NSLog(@"setName");
if (_name != name) {
[_name release];
_name = [name copy];
}
}
//代碼四:
@synthesize name = _name;? //這裏你本身定義了getter和setter,這句話沒用了,你能夠註釋掉。
-(NSString*) name{
NSLog(@"name");
return _name;
}
-(void) setName:(NSString *)name{
NSLog(@"setName");
if (_name != name) {
[_name release];
_name = [name copy];
}
}
上面這四段代碼也是等價的。看到這裏,你們對Property的做用相信會有更加進一步的理解了吧。可是,你必須當心,你若是使用了Property,並且你本身又重寫了setter/getter的話,你須要清楚的明白,你究竟幹了些什麼事。別寫出下面的代碼,雖然是合法的,可是會誤導別人:
//BaseClass.h
@interface BaseClass : NSObject{
@public
NSArray *_names;
}
@property(nonatomic,assgin,readonly) NSArray *names;? //注意這裏是assign
-(void) setNames:(NSArray*)names;
//BaseClass.m
@implementation BaseClass
@synthesize names = _names;
-(NSArray*) names{
NSLog(@"names");
return _names;
}
-(void) setNames:(NSArray*)names{
NSLog(@"setNames");
if (_name != name) {
[_name release];
_name = [name retain];? //你retain,可是你不覆蓋這個方法,那麼編譯器會生成setNames方法,裏面確定是用的assign
}
}
當別人使用@property來作內存管理的時候就會有問題了。總結一下,若是你本身實現了getter和setter的話,atomic/nonatomic/retain/assign/copy這些只是給編譯的建議,編譯會首先會到你的代碼裏面去找,若是你定義了相應的getter和setter的話,那麼好,用你的。若是沒有,編譯器就會根據atomic/nonatomic/retain/assign/copy這其中你指定的某幾個規則去生成相應的getter和setter。
好了,說了這麼多,回到咱們的正題吧。atomic和nonatomic的做用與區別:
若是你用@synthesize去讓編譯器生成代碼,那麼atomic和nonatomic生成的代碼是不同的。若是使用atomic,如其名,它會保證每次getter和setter的操做都會正確的執行完畢,而不用擔憂其它線程在你get的時候set,能夠說保證了某種程度上的線程安全。可是,我上網查了資料,僅僅靠atomic來保證線程安全是很天真的。要寫出線程安全的代碼,還須要有同步和互斥機制。
而nonatomic就沒有相似的「線程安全」(我這裏加引號是指某種程度的線程安全)保證了。所以,很明顯,nonatomic比atomic速度要快。這也是爲何,咱們基本上全部用property的地方,都用的是nonatomic了。
還有一點,可能有讀者常常看到,在個人教程的dealloc函數裏面有這樣的代碼:self.xxx = nil;看到這裏,如今大家明白這樣寫有什麼用了吧?它等價於[xxx release];? xxx = [nil retain];(---若是你的property(nonatomic,retian)xxx,那麼就會這樣,若是不是,就對號入座吧)。
由於nil能夠給它發送任何消息,而不會出錯。爲何release掉了還要賦值爲nil呢?你們用c的時候,都有這樣的編碼習慣吧。
int* arr = new int[10];??? 而後不用的時候,delete arr; arr = NULL;? 在objc裏面能夠用一句話self.arr = nil;搞定。程序員
本文轉自:http://www.spasvo.com/news/html/20141121145332.htmlweb