您的位置:首页 > 移动开发 > Objective-C

Objective-C的@property 详解(retain是指针拷贝,copy是内容拷贝,★firecat推荐★)

2015-02-11 14:34 330 查看
文章来源:http://www.cnblogs.com/andyque/archive/2011/08/03/2125728.html@property与@synthesize是成对出现的,可以自动生成某个类成员变量的存取方法。在Xcode4.5以及以后的版本,@synthesize可以省略。之前很多网友对我翻译的教程中的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马上提示错误,请看截图:  请大家注意看出错提示“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的对象,即:垃圾回收 (garbage collection, 简称gc),所以默认的assign修饰符是不行的。那么什么时候用assign、什么时候用retain和copy呢?推荐做法是NSString用copy,delegate用assign(且一定要用assign,不要问为什么,只管去用就是了,以后你会明白的),非objc数据类型,比如int,float等基本数据类型用assign(默认就是assign),而其它objc类型,比如NSArray,NSDate用retain。深拷贝copy:NSString //类似C++的string类,需要深拷贝 浅拷贝retain:NSArray,NSDate 默认assign:delegate,int,float,double,NSInteger,id  在继续之前,我还想补充几个问题,就是如果我们自己定义某些变量的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;搞定。    讲了这么多,,如果大家还有什么问题,欢迎在下方留言,我有时间一定会忙回复你的。一直看我文章的朋友们,如果知道答案也帮忙回答一下哈,先谢谢你们啦!  接下来,我会翻译Ray写的关于objc内存管理和使用property作内存管理的文章,敬请期待! --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------声明property的语法为:@property (参数1,参数2) 类型 名字;如:@property(nonatomic,retain) UIWindow *window;其中参数主要分为三类:读写属性: (readwrite/readonly)setter语意:(assign/retain/copy)原子性: (atomicity/nonatomic)各参数意义如下:readwrite产生setter\getter方法,默认是可读可写readonly只产生简单的getter,没有setter。assign默认类型,setter方法直接赋值,而不进行retain操作。assign生成的赋值函数是简单地为变量赋值。例如:- (void)setFirstName:(NSString *)inValue{firstName = inValue;}retainsetter方法对参数进行release旧值,再retain新值。retain生成的赋值函数在赋值到变量时会保留传入的参数。例如:- (void)setFirstName:(NSString *)inValue{[firstName autorelease];firstName = [inValue retain];}copysetter方法进行Copy操作,与retain一样。copy生成的存取器函数会复制传入值到成员变量。例如:- (void)setFirstName:(NSString *)inValue{[firstName autorelease];firstName = [inValue copy];}atomicity默认是原子性的,线程安全的。例如:- (NSString *)firstName{[threadLock lock];NSString *result = [[firstName retain] autorelease];[threadLockunlock];returnresult;}nonatomic非线程安全的,变量保护,提高性能。例如:- (NSString *)firstName{NSString *result = [[firstName retain] autorelease];return result;}--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------copy其实是建立了一个相同的对象,而retain不是:比如一个NSString对象,地址为0×1111,内容为@”STR”;copy到另外一个NSString之后,地址为0×2222,内容相同,新的对象retain为1,旧有对象没有变化;retain到另外一个NSString之后,地址相同(建立一个指针,指针拷贝),内容当然相同,这个对象的retain值+1;也就是说,retain是指针拷贝,copy是内容拷贝。Bar *foo = [[Bar alloc] init]; //执行完本语句,foo对象的保留计数是1。[foo retain]; //执行完本语句,foo对象的保留计数是2。[foo release]; //执行完本语句,foo对象的保留计数是1。copy是内容的拷贝,对于像NSString,的确是这样.但是,如果是copy的是一个NSArray呢?比如,NSArray *array = [NSArray arrayWithObjects:@"hello",@"world",@"baby"];NSArray *array2 = [array copy];这个时候,,系统的确是为array2开辟了一块内存空间,但是我们要认识到的是,array2中的每个元素,,只是copy了指向array中相对应元素的指针.这便是所谓的"浅复制".了解到这一点非常重要....---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------@property与@synthesize是成对出现的,可以自动生成某个类成员变量的存取方法。在Xcode4.5以及以后的版本,@synthesize可以省略。1.atomic与nonatomicatomic:默认是有该属性的,这个属性是为了保证程序在多线程情况,编译器会自动生成一些互斥加锁代码,避免该变量的读写不同步问题。nonatomic:如果该对象无需考虑多线程的情况,请加入这个属性,这样会让编译器少生成一些互斥加锁代码,可以提高效率。2.readwrite与readonlyreadwrite:这个属性是默认的情况,会自动为你生成存取器。readonly:只生成getter不会有setter方法。readwritereadonly这两个属性的真正价值,不是提供成员变量访问接口,而是控制成员变量的访问权限。3.strong与weakstrong:强引用,也是我们通常说的引用,其存亡直接决定了所指向对象的存亡。如果不存在指向一个对象的引用,并且此对象不再显示在列表中,则此对象会被从内存中释放。weak:弱引用,不决定对象的存亡。即使一个对象被持有无数个弱引用,只要没有强引用指向它,那么还是会被清除。strongretain功能相似;weakassign相似,只是当对象消失后weak会自动把指针变为nil;4.assign、copy、retainassign:默认类型,setter方法直接赋值,不进行任何retain操作,不改变引用计数。一般用来处理基本数据类型。retain:释放旧的对象(release),将旧对象的值赋给新对象,再令新对象引用计数为1。我理解为指针的拷贝,拷贝一份原来的指针,释放原来指针指向的对象的内容,再令指针指向新的对象内容。copy:与retain处理流程一样,先对旧值release,再copy出新的对象,retainCount为1.为了减少对上下文的依赖而引入的机 制。我理解为内容的拷贝,向内存申请一块空间,把原来的对象内容赋给它,令其引用计数为1。对copy属性要特别注意:被定义有copy属性的对象必须要 符合NSCopying协议,必须实现- (id)copyWithZone:(NSZone *)zone方法。也可以直接使用:使用assign: 对基础数据类型 (NSInteger,CGFloat)和C数据类型(int, float, double, char, 等等)使用copy: 对NSString使用retain: 对其他NSObject和其子类5.getter settergetter:是用来指定get方法的方法名setter:是用来指定set访求的方法名在@property的属性中,如果这个属性是一个BOOL值,通常我们可以用getter来定义一个自己喜欢的名字,例如:@property (nonatomic, assign, getter=isValue) boolean value;@property (nonatomic, assign, setter=setIsValue) boolean value;一,retain,copy, assign区别1. 假设你用malloc分配了一块内存,并且把它的地址赋值给了指针a,后来你希望指针b也共享这块内存,于是你又把a赋值给(assign)了b。此时a和b指向同一块内存,请问当a不再需要这块内存,能否直接释放它?答案是否定的,因为a并不知道b是否还在使用这块内存,如果a释放了,那么b在使用这块内存的时候会引起程序crash掉。2. 了解到1中assign的问题,那么如何解决?最简单的一个方法就是使用引用计数(reference  counting),还是上面的那个例子,我们给那块内存设一个引用计数,当内存被分配并且赋值给a时,引用计数是1。当把a赋值给b时引用计数增加到 2。这时如果a不再使用这块内存,它只需要把引用计数减1,表明自己不再拥有这块内存。b不再使用这块内存时也把引用计数减1。当引用计数变为0的时候,代表该内存不再被任何指针所引用,系统可以把它直接释放掉。3. 上面两点其实就是assign和retain的区别,assign就是直接赋值,从而可能引起1中的问题,当数据为int, float等原生类型时,可以使用assign。retain就如2中所述,使用了引用计数,retain引起引用计数加1, release引起引用计数减1,当引用计数为0时,dealloc函数被调用,内存被回收。4. copy是在你不希望a和b共享一块内存时会使用到。a和b各自有自己的内存。5. atomic和nonatomic用来决定编译器生成的getter和setter是否为原子操作。在多线程环境下,原子操作是必要的,否则有可能引起错误的结果。加了atomic,setter函数会变成下面这样:if (property != newValue) {[property release];property = [newValue retain];}二,深入理解一下(包括autorelease)1. retain之后count加一。alloc之后count就是1,release就会调用dealloc销毁这个对象。如果 retain,需要release两次。通常在method中把参数赋给成员变量时需要retain。例如:ClassA有 setName这个方法:-(void)setName:(ClassName *) inputName{name = inputName;[name retain]; //此处retian,等同于[inputName retain],count等于2}调用时:ClassName *myName = [[ClassName alloc] init];[classA setName:myName]; //retain count == 2[myName release]; //retain count==1,在ClassA的dealloc中release name才能真正释放内存。2. autorelease 更加tricky,而且很容易被它的名字迷惑。我在这里要强调一下:autorelease不是garbage collection,完全不同于Java或者.Net中的GC。autorelease和作用域没有任何关系!autorelease 原理:a.先建立一个autorelease poolb.对象从这个autorelease pool里面生成。c.对象生成 之后调用autorelease函数,这个函数的作用仅仅是在autorelease pool中做个标记,让pool记得将来release一下这个对象。d.程序结束时,pool本身也需要rerlease, 此时pool会把每一个标记为autorelease的对象release一次。如果某个对象此时retain count大于1,这个对象还是没有被销毁。上面这个例子应该这样写:ClassName *myName = [[[ClassName alloc] init] autorelease];//标记为autorelease[classA setName:myName]; //retain count == 2[myName release]; //retain count==1,注意,在ClassA的dealloc中不能release name,否则release pool时会release这个retain count为0的对象,这是不对的。记住一点:如果这个对象是你alloc或者new出来的,你就需要调用release。如果使用autorelease,那么仅在发生过retain的时候release一次(让retain count始终为1)。3. xcode 中的新标记 strong weakstrong 用来修饰强引用的属性;对应以前retainweak 用来修饰弱引用的属性;对应以前的assignstrong与weak是由ARC新引入的对象变量属性 。Xcode4.2(ios sdk4.3和以下版本)和之前的版本使用的是retain和assign,老版本是不支持ARC的。xcode 4.3(ios5和以上版本)之后就有了ARC,并且开始使用 strong与weak。assign: 用于非指针变量。用于基础数据类型 (例如NSInteger)和C数据类型(int,float, double, char, 等),另外还有id如:@property (nonatomic, assign) int number;@property (nonatomic, assign) id className;//id必须用assign反正记住:前面不需要加 “*” 的就用assign吧retain:用于指针变量。就是说你定义了一个变量,然后这个变量在程序的运行过程中会被更改,并且影响到其他方法。一般是用于字符串( NSString,NSMutableString),数组(NSMutableArray,NSArray),字典对象,视图对象(UIView ),控制器对象(UIViewController)等比如:@property (nonatomic,retain) NSString * myString;@property (nonatomic, retain) UIView * myView;@property (nonatomic, retain) UIViewController * myViewController;xcode 4.2不支持ARC,所以会频繁使用retain来修饰,用完释放掉,而xcode4.3支持ARC,可以使用retian,不需要手动释放内存,系统会自动为你完成,如果你在xcode4.3以上版本开发,retain和strong都是一样的,没区别。strong和weak: 事实上 @property(nonatomic,strong)MyClass *myObject;就是相当于@property(nonatomic,retain) MyClass *myObject;@property(nonatomic, weak )id<RNNewsFeedCellDelegate>delegate;就是相当于@property(nonatomic,assign)id<RNNewsFeedCellDelegate>delegate;现在系统自动生成的属性都是用weak来修饰的,我想应该是xcode 4.2不支持ARC,所以大家都是用retain。现在xcode4.3支持ARC了,于是苹果建议程序员放弃retain,以后都用weak。 weak就是相当于assign,同样可以在xcode4.3开发环境下放弃使用assign 使用weak 来代替。unsafe_unretained: unsafe_unretained就是ios5版本以下的 assign ,也就是 unsafe_unretained , weak, assign 三个都是一个样的。 因为 ios5用的是 weak ,那在ios4.3就用不了,如果你将 weak 修改为 unsafe_unretained ,那就可以用了。说到底就是iOS 5之前的系统用该属性代替 weak 来使用。copy:这个东西估计是大部分人最不容易搞明白的东西,我也搞不明白。听别人说这个东西基本不用了,效果其实和retain没什么两样,唯一的区别就是copy只用于NSString而不能用于NSMutableString。不过好像当一个类继承NSObject,那么这个类里面的属性需要使用copy,比如:#import <Foundation/Foundation.h>#import <MapKit/MKAnnotation.h>@interface Annotation : NSObject <MKAnnotation> {CLLocationCoordinate2D coordinate;NSString *title;NSString *subtitle;}@property (nonatomic) CLLocationCoordinate2D coordinate;@property (nonatomic, copy) NSString *title;@property (nonatomic, copy) NSString *subtitle;@end反正以后就这么用就是了。反正就记住一点:xcode4.2之前用retain和assign;xcode4.3或以上版本用strong与weak 。以前用xcode4.2开发程序的程序员会习惯用retain ,所以代码都是retian的,新手如果从xcode4.3学起的话就用strong与weak吧,这里面讲的区别有些不对的地方。readonly此标记说明属性是只读的,默认的标记是读写,如果你指定了只读,在@implementation中只需要一个读取器。或者如果你使用@synthesize关键字,也是有读取器方法被解析。而且如果你试图使用点操作符为属性赋值,你将得到一个编译错误。readwrite此标记说明属性会被当成读写的,这也是默认属性。设置器和读取器都需要在@implementation中实现。如果使用@synthesize关键字,读取器和设置器都会被解析。assign此标记说明设置器直接进行赋值,这也是默认值。在使用垃圾收集的应用程序中,如果你要一个属性使用assign,且这个类符合NSCopying协议,你就要明确指出这个标记,而不是简单地使用默认值,否则的话,你将得到一个编译警告。这再次向编译器说明你确实需要赋值,即使它是可拷贝的。retain指定retain会在赋值时唤醒传入值的retain消息。此属性只能用于Objective-C对象类型,而不能用于Core Foundation对象。(原因很明显,retain会增加对象的引用计数,而基本数据类型或者Core Foundation对象都没有引用计数——译者注)。copy它指出,在赋值时使用传入值的一份拷贝。拷贝工作由copy方法执行,此属性只对那些实行了NSCopying协议的对象类型有效。更深入的讨论,请参考“复制”部分。nonatomic指出访问器不是原子操作,而默认地,访问器是原子操作。这也就是说,在多线程环境下,解析的访问器提供一个对属性的安全访问,从获取器得到的返回值或者通过设置器设置的值可以一次完成,即便是别的线程也正在对其进行访问。如果你不指定nonatomic,在自己管理内存的环境中,解析的访问器保留并自动释放返回的值,如果指定了nonatomic,那么访问器只是简单地返回这个值。所以property的属性默认是:readwrite,assign, atomic(没有这个关键字)-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------1,assign :简单赋值,不更改索引计数假设你用malloc分配了一块内存,并且把它的地址赋值给了指针a,后来你希望指针b也共享这块内存,于是你又把a赋值给(assign)了b。此时a 和b指向同一块内存,请问当a不再需要这块内存,能否直接释放它?答案是否定的,因为a并不知道b是否还在使用这块内存,如果a释放了,那么b在使用这块内存的时候会引起程序crash掉应用场合:对基础数据类型 (例如NSInteger,CGFloat)和C数据类型(int, float, double, char, 等)适用简单数据类型2,retain:与strong相对应,使用了引用计数,retain+1,release -1;当引用 计数为0时,dealloc会被调用,内存被释放3,copy:用于非共享内存时,每个指针有自己的内存空间4,atomic//默认属性A,当一个变量声明为atomic时,意味着在多线程中只能有一个线程能对它进行访问B,当一个变量声明为atomic时,该变量为线程安全型,但是会影响访问速度,C,当一个变量声明为atomic时,在非ARC编译环境下,需要设置访问锁来保证对该变量进行正确的get/set5,nonatomicA,    当一个变量声明为nonatomic时,意味着多个线程可以同时对其进行访问B,    当一个变量声明为nonatomic时,它是非线程安全型,访问速度快;C,    当一个变量声明为nonatomic时,当两个不同的线程对其访问时,容易失控。总结:atomic和nonatomic用来决定编译器生成的getter和setter是否为原子操作。在多线程环境下,原子操作是必要的,否则有可能引起错误的结果。加了atomic,setter函数会变成下面这样:
if (property != newValue) {[property release];property = [newValue retain];}6.strong://ARC中默认属性,等于非ARC中的retain与retain相对应,应用场景:strong属性用于ARC中@property (strong,nonatomic) ViewController *viewController;//strong用于主窗口7,weak:与assign 相对应,应用场景:用于IBOutlets,如,UIViewController的子类,即一般的控件。@property (weak, nonatomic) IBOutlet UIButton *myButton;//weak用于子控件子控件要用weak指针,因为它已经在这个窗口上,有一个strong指针指向它了。所以只需要一个weak指针就行了。IBOutlet 这个类型没有具体的内容,只是Xcode用来跟踪哪个property是Outlet。 编译器会忽略它,没有什么实际内容。weak的意思,我感觉就是一定有一个strong在指向他,然后有一堆weak也指向他,如果strong一释放,一堆weak就全nil了。strong与weak的区别举例:前提:我们把要用strong或者weak的对象比作一只风筝,风筝想挣脱线的束缚,自由飞翔去,如果此时有一根线,那么这只风筝就挣脱不了过程分析strong属性的变量:当我们把指向一只风筝的变量声明为strong时,此时,你就拥有控制这只风筝的线,假如此时有五个人同时控制这只风筝(即这只风筝对象有三个strong类型的变量指向它),那么只有一种情况,这只风筝才会挣脱掉线的束缚:这三个人都放掉手中的线,(release掉)weak属性的变量:当我们把指向一只风筝的变量声明为weak时,此时,就像站在旁边看风筝的观众们一样,当上面的三个人还握着手中的线时,他们只能看到风筝,并不能控制它,他们能做的只能是用手指指向风筝,并大喊,“看,那只风筝飞得真高!”,然而,当上面的三个人把手中的线都放掉时,此时,风筝飞走了,看不见了,不管有再多的观众,他们再也看不到风筝了,这个故事告诉我们一个道理:当strong类型的指针被释放掉之后,所有的指向同一个对象的weak指针都会被清零。8,readonly只有get方法,没有set方法9,readwrite//默认属性有get/set方法10,unsafe_unretauined用在ARC编译环境下,在此环境下,与assign相似。它只是告诉ARC如何正确地调用声明为unsafe_unretauined变量的retain和release
                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: