在创建属性时,总是习惯的创建nonatomic
类型属性,也不管为什么这么用。其实在以往准备面试的时候,也只是理解个大概,再背诵一下,并没有真正的理解atomic
和nonatomic
的区别,今天在和前辈讨论属性修饰的时候,突然开悟,立马写博客记录下来。
区别
先把他们的区别陈述一下:
atomic
:系统生成的 getter/setter 会保证 get、set 操作的完整性,不受其他线程影响。但是atomic
的速度慢。nonatomic
:无法保证get、set 操作的完整性。速度比atomic
快很多。
很难懂,没关系。
使用atomic一定是线程安全的吗?
不是的,只能保证 get、set 操作的完整性,但不能保证线程的安全。
还是不懂,看举例。
举例
代码
1 | @interface ELTestModel : NSObject |
1 | ELTestModel *model = [[ELTestModel alloc] init]; |
分析
如果这个atomic
能保证线程安全,那么打印结果应该是先全为11111111
,直到执行第二个for循环后,才会打印22222222
。
但是实际情况是:随机打印。
OK,现在一个问题已经说明,那么系统生成的 getter/setter 会保证 get、set 操作的完整性
怎么理解呢。
其实是这样的,如果是atomic
修饰的对象,在setter或者getter方法内部,会开启一个锁,来防止赋值或者取值时出现问题。这也就是系统生成的 getter/setter 会保证 get、set 操作的完整性
的意思。
像这样:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16- (void)setName:(NSString *)name
{
@synchronized(self) {
if (_name != name) {
[_name release];
_name = [name copy];
}
}
}
- (NSString *)name
{
@synchronized(self) {
return _name;
}
}
引伸
仍然是上述代码,怎么才能做到线程安全呢。其实很简单,在线程内部加锁。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
@synchronized(self) {
for (int i = 0; i < 999; i++) {
NSLog(@"1--");
model.name = @"11111111";
}
}
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
@synchronized(self) {
for (int i = 0; i < 999; i++) {
NSLog(@"2--");
model.name = @"22222222";
}
}
});
for (int i = 0; i < 9999; i++) {
NSLog(@"%@", model.name);
}
最后
atomic
不能保证线程安全,只能保证set/get的时候安全,就相当于是在set/get时候,在代码上下加了锁,而不是在线程的开头和结尾加了锁。