虽然写了不少年的iOS代码,可是不少东西没有深刻理解,或者当时理解了,后来不用又慢慢又忘了。因此抽空整理一份资料,以备本身之后查找。也但愿看到的小伙伴批评指正。
这篇文章主要写@property后面的修饰符。html
assign:既能够修饰对象,也能够修饰基本类型。unsafe_unretained与assign同义。assign修饰对象会产生野指针的问题,修饰的对象释放后,指针不会自动置成nil,此时再向对象发消息程序会崩溃。安全
weak:只能修饰对象,若是修饰基本数据类型会报错:Property with 'weak' attribute must be of object type. weak修饰的对象释放后(引用计数变为0),指针会被自动置为nil,以后再向该对象发消息也不会崩溃(向nil发送任何消息都不会崩溃)。weak适用于delegate和block等引用类型,不会致使野指针问题,也不会循环引用,很是安全。app
总结:
assign、weak和unsafe_unretained,修饰的属性,再调用set方法时是不会增长引用计数的。能够理解为set方法只是简单的赋值。也就是栈上的变量的赋值。验证代码以下:atom
@property(nonatomic,assign) NSObject *property; //在代码实现中 NSObject *object = [[NSObject alloc] init]; NSLog(@"\n 引用计数 = %lu \n ", (unsigned long)[object retainCount]); self.property = object; NSLog(@"\n 引用计数 = %lu \n ", (unsigned long)[object retainCount]);
输出结果
2019-08-05 02:02:08.475192+0800 test4[89384:3597709]
引用计数 = 1
2019-08-05 02:02:08.475320+0800 test4[89384:3597709]
引用计数 = 1 线程
因此assign、weak和unsafe_unretained修饰的对象,默认生成的set方法,应该以下方法一所示:
-(void)setProperty:(NSObject *)property{指针
_property = property; //weak可能还有其余一些操做
}code
而retain和strong修饰的对象,大概生成的set方法以下方法二所示:
-(void)setProperty:(NSObject *)property{htm
[property retain]; [_property release]; _property = property;
}对象
注意:
若是本身实现了set方法,则这些关于存取方法的修饰符就再也不起做用了。好比,你虽然用assign修饰了对象,可是本身实现了set方法二,则assign会被忽略。ci
在苹果的文档里有这样一句话:
// The following declaration is a synonym for: @property(retain) MyClass *myObject;
@property(strong) MyClass *myObject;
因此在ARC下他们是等价的。
一般在ARC环境里,不会用assign修饰对象,由于有了weak,再用assign就是自找麻烦。总之在ARC环境里用strong代替retain强引用对象,用weak弱引用对象,用assign修饰基本数据类型。
atomic和nonatomic的区别在于:系统自动生成的 getter/setter 方法不同。(若是你本身写 getter/setter,那 atomic/nonatomic/retain/assign/copy 这些关键字只起提示做用,写不写都同样)
对于atomic的属性,系统生成的 getter/setter 会保证 get、set 操做的完整性,不受其余线程影响。好比,线程 A 的 getter 方法运行到一半,线程 B 调用了 setter:那么线程 A 的 getter 仍是能获得一个无缺无损的对象。
而nonatomic就没有这个保证了。因此,nonatomic的速度要比atomic快。
nonatomic系统合成的方法大概以下:
@property(nonatomic, retain) UITextField *userName; //系统生成的代码以下: - (UITextField *) userName { return userName; } - (void) setUserName:(UITextField *)userName_ { [userName_ retain]; [userName release]; userName = userName_; }
atomic系统合成的方法大概以下:
//@property(retain) UITextField *userName; //系统生成的代码以下: - (UITextField *) userName { UITextField *retval = nil; @synchronized(self) { retval = [[userName retain] autorelease]; } return retval; } - (void) setUserName:(UITextField *)userName_ { @synchronized(self) { [userName release]; userName = [userName_ retain]; } }
假设有一个 atomic 的属性 "userName",若是线程 A 调[self setUserName:@"A"],线程 B 调[self setUserName:@"B"],线程 C 调[self userName],那么全部这些不一样线程上的操做都将依次顺序执行——也就是说,若是一个线程正在执行 getter/setter,其余线程就得等待。所以,属性 setUserName 是读/写安全的。
可是,若是有另外一个线程 D 同时在调[userName release],那可能就会crash,由于 release 不受 getter/setter 操做的限制。也就是说,这个属性只能说是读/写安全的,但并非线程安全的,由于别的线程还能进行读写以外的其余操做。线程安全须要开发者本身来保证。
若是 userName 属性是 nonatomic 的,那么上面例子里的全部线程 A、B、C、D 均可以同时执行,可能致使没法预料的结果。若是是 atomic 的,那么 A、B、C 会串行,而 D 仍是并行的。
readwrite:默认的属性修饰符。会合成get和set方法。
readonly:只会生成get方法。因此若是obj用readonly修饰,则self.obj = xxx;编译时会报错提示:“Assignment to readonly property”。
copy:只能修饰对象类型,不能修饰基础数据类型(用了会报编译时错误)。用copy修饰的对象,必须实现
NSCopying协议也就是实现方法-(id)copyWithZone:(nullable NSZone *)zone。合成的set方法中会调用这个方法。这里也能看出copy,和retain不同的地方。
至于copy是深拷贝仍是浅拷贝彻底是看copyWithZone的实现方式。也就是说copy和深拷贝和浅拷贝没有关系!
另外对象的mutableCopy方法,也是要对象知足NSMutableCopying协议,实现- (id)mutableCopyWithZone:(nullable NSZone *)zone方法。若是不实现次方法会运行时报错!
总结
修饰符其实就是告诉编译器怎么来合成存取方法的。有些修饰符须要本身另外实现一些方法,好比copy,这里就能定制一些本身的东西,好比实现真正的多层深拷贝等等。
另外,还有一些新的修饰符nullable、nonnull、null_resettable、null_unspecified、getter=method、setter=method等,咱们下一篇文章里再总结.