我们在声明一个属性的时候,经常性会把每个属性定义为nonatomic(非原子性),很少用atomic。原因就是属性定义为atomic也不能保证线程安全,而且atomic加了同步锁,性能较nonatomic慢。
此时问题就来了,原子性是什么呢, 设置了之后在哪里加了锁?
实际上,如果一个属性定义为atomic,那么它的setter和getter会通过锁的方式来确保两个方法完整执行。也就是说,如果两个线程同时读取一个属性,那么无论何时,都能读取有效的属性值。
@interface TestObj : NSObject
@property (atomic, copy) NSString *firstName;
TestObj *obj = [[TestObj alloc]init];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// NSLog(@"obj1 first: %@",obj.firstName);
obj.firstName = @"zhu";
NSLog(@"obj1: %@",obj.firstName);
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// NSLog(@"obj2 first: %@",obj.firstName);
// obj.firstName = @"xixi";
NSLog(@"obj2: %@",obj.firstName);
});
打印信息
2018-05-22 16:17:01.658839+0800 testIndex[727:19821729] obj2: zhu
2018-05-22 16:17:01.658839+0800 testIndex[727:19821728] obj1: zhu
假设不加锁的话
@interface TestObj : NSObject
@property (nonatomic, copy) NSString *firstName;
那么上面那段代码可能就会出现
2018-05-22 16:18:55.476249+0800 testIndex[793:19841361] obj2: (null)
2018-05-22 16:18:55.476273+0800 testIndex[793:19841362] obj1: zhu
那么atomic就是可靠的吗,nonatomic是非原子性的,本来就是非线程安全的,那么我们为啥还要用nonatomic。
其实atomic和nonatomic也就是setter和getter上操作的不同
nonatomic
- (void)setFirstName:(NSString *)firstName
{
if (_firstName != firstName) {
[_firstName release];
_firstName = [firstName retain];
}
}
- (NSString *)firstName
{
return _firstName;
}
atomic的实现
- (void)setFirstName:(NSString *)firstName
{
@synchronized(self) {
if (_firstName != firstName) {
[_firstName release];
_firstName = [firstName retain];
}
}
}
- (NSString *)firstName
{
@synchronized(self) {
return _firstName;
}
}
atomic时,虽然对属性的读和写是原子性的,但是仍可能出现线程错误。假设线程A对firstName进行写操作,但同时线程B也进行写操作,虽然两个写操作是依次进行的,但是如果线程A如果有读操作,可能执行获取到的firstName并不是线程A写的数据,这就不是线程安全了。
不加lock
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// [lock lock];
obj.firstName = @"zhu";
NSLog(@"obj1: %@",obj.firstName);
// [lock unlock];
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// [lock lock];
obj.firstName = @"xixi";
NSLog(@"obj2: %@",obj.firstName);
// [lock unlock];
});
输出可能是
2018-05-22 16:48:15.663898+0800 testIndex[1157:20091711] obj1: xixi
2018-05-22 16:48:15.663898+0800 testIndex[1157:20091710] obj2: xixi
加了锁之后
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[lock lock];
obj.firstName = @"zhu";
NSLog(@"obj1: %@",obj.firstName);
[lock unlock];
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[lock lock];
obj.firstName = @"xixi";
NSLog(@"obj2: %@",obj.firstName);
[lock unlock];
});
输出是
2018-05-22 16:49:04.088074+0800 testIndex[1185:20099838] obj1: zhu
2018-05-22 16:49:04.088232+0800 testIndex[1185:20099837] obj2: xixi
其实证明了我们使用了atomic还不能保证线程安全性,又用了同步锁,耗费性能,所以我们通过nonatomic关键字居多,线程安全通过加lock保证。