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? [email protected]"set?base name";
   NSLog(@ "base class‘s name = %@", base.name);
   不用等你编译,xcode4马上提示错误,请看截图:

  请大家注意看出错提示“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];
}
}

  现在,你再编译运行,一样工作的很好。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;那么将会报一个警告,如下图:

  因为是非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.html

时间: 2024-11-09 09:56:22

IOS开发之property详解的相关文章

ios开发之 NSObject详解

NSObject是大部分Objective-C类继承体系的根类.这个类遵循NSObject协议,提供了一些通用的方法,对象通过继承NSObject,可以从其中继承访问运行时的接口,并让对象具备Objective-C对象的基本能力.下面我们就详细的介绍NSObject. 一.使用详解 1.加载及初始化类 /** 运行时加载类或分类调用该方法, 每个类只会调用一次 */ + (void)load { } /** 类实例化使用前需要先初始化, 一个类调用一次, 如果子类没有实现该方法则会调用父类方法

IOS开发之UIScrollViewDelegate详解

//scrollView滚动时,就调用该方法.任何offset值改变都调用该方法.即滚动过程中,调用多次 - (void)scrollViewDidScroll:(UIScrollView *)scrollView { NSLog(@"scrollViewDidScroll"); CGPoint point=scrollView.contentOffset; NSLog(@"%f,%f",point.x,point.y); // 从中可以读取contentOffse

Android开发之SpannableString详解

在实际的应用开发过程中经常会遇到,在文本的不同部分显示一些不同的字体风格的信息如:文本的字体.大小.颜色.样式.以及超级链接等.一般情况下,TextView中的文本都是一个样式,对于类似的情况,可以借助SpannableString或SpannableStringBuilder对象来实现以上设置. SpannableString与SpannableStringBuilder都可以将某段文本设置成一个Span,在Android中,Span表示一段文本的效果,例如,链接形式.图像.带背景的文本等.只

Android开发之InstanceState详解

Android开发之InstanceState详解 本文介绍Android中关于Activity的两个神秘方法:onSaveInstanceState() 和 onRestoreInstanceState(),并且在介绍这两个方法之后,再分别来实现使用InstanceState保存和恢复数据功能.Android实现屏幕旋转异步下载效果这样两个示例. 首先来介绍onSaveInstanceState() 和 onRestoreInstanceState() .关于这两个方法,一些朋友可能在Andr

Android开发之WebView详解

概述: 一个显示网页的视图.这个类是你可以滚动自己的Web浏览器或在你的Activity中简单地显示一些在线内容的基础.它使用了WebKit渲染引擎来显示网页,包括向前和向后导航的方法(通过历史记录),放大和缩小,执行文本搜索等. 需要注意的是:为了让你的应用能够使用WebView访问互联网和加载网页,你必须添加Internet的权限在Android Manifest文件中: <uses-permission android:name="android.permission.INTERNE

【Android】Android软件开发之ListView 详解

原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://xys289187120.blog.51cto.com/3361352/657171 ListView的使用方法 ListView是Android软件开发中非常重要组件之一,基本上是个软件基本都会使用ListView ,今天我通过一个demo来教大家怎么样使用ListView组件 绘制出漂亮的列表,说道ListView就不得不说Adapter适配器,因为只有通过Adapter才可

Android软件开发之TextView详解

Android软件开发之TextView详解<IGNORE_JS_OP> TextView的API 中文文档中 说明它的结构 结构java.lang.Object   android.view.View        android.widget.TextView直接子类:    Button, CheckedTextView, Chronometer, DigitalClock, EditText间接子类:     AutoCompleteTextView, CheckBox, Compou

Android开发之BroadcastReceiver详解

BroadcastReceiver,顾名思义就是"广播接收者"的意思,它是Android四大基本组件之一,这种组件本质上是一种全局的监听器,用于监听系统全局的广播消息.它可以接收来自系统和应用的的广播. 由于BroadcastReceiver是一种全局的监听器,因此它可以非常方便地实现系统不同组件之间的通信.比如Activity与通过startService()方法启动的Service之间通信,就可以借助于BroadcastReceiver来实现. BroadcastReceiver简

Android开发之InstanceState详解(转)---利用其保存Activity状态

Android开发之InstanceState详解 本文介绍Android中关于Activity的两个神秘方法:onSaveInstanceState() 和 onRestoreInstanceState(),并且在介绍这两个方法之后,再分别来实现使用InstanceState保存和恢复数据功能.Android实现屏幕旋转异步下载效果这样两个示例. 首先来介绍onSaveInstanceState() 和 onRestoreInstanceState() .关于这两个方法,一些朋友可能在Andr