IOS开发之property详解

  以前不少网友对我翻译的教程中的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

相关文章
相关标签/搜索