一般情况下,我们定义属性的时候都是这样定义的:
@property (nonatomic, copy) NSString *string1;
@property (nonatomic, strong) NSMutableString *string2;
copy
和strong
的区别就不在这里多说了,主要来看下这个nonatomic
以及atomic
nonatomic & atomic
atomic和nonatomic用来决定编译器生成的getter和setter是否为原子操作。这里是指系统自动生成的 getter/setter 方法。如果自己重写 getter/setter方法,那atomic/nonatomic只起提示作用
-
atomic:提供多线程安全
设置成员变量的@property属性时,默认为atomic,提供多线程安全。相当于函数头尾加了锁一样,可以保证数据的完整性。而这种机制是耗费系统资源的。
{lock}
if (property != newValue) {
[property release];
property = [newValue retain];
}
{unlock}
-
nonatomic:禁止多线程,变量保护,提高性能。
一般iOS程序中,所有属性都声明为nonatomic。这样做的原因是:在iOS中使用同步锁的开销比较大, 这会带来性能问题。一般情况下并不要求属性必须是“原子的”,因为这并不能保证“线程安全”(thread safety),若要实现“线程安全”的操作,还需采用更为深层的锁定机制才醒。
于是引出了本篇的话题:线程锁
线程锁
多线程编程中,应该尽量避免资源在线程之间共享,以减少线程间的相互作用。 但是总是有多个线程相互干扰的情况(如多个线程访问一个资源)。在线程必须交互的情况下,就需要一些同步工具,来确保当它们交互的时候是安全的。
简单来说:我们引入锁的目的是为了线程安全。
image找到一张关于线程锁性能的比较图片,如图所示
性能最好的是OSSpinLock(据说是不安全的的,详情请看ibireme大神的不再安全的 OSSpinLock)
我们一起来看下在iOS开发中常用的几种锁
1. @synchronized
推荐文章:
Peak大神正确使用多线程同步锁@synchronized()
关于 @synchronized,这儿比你想知道的还要多
@synchronized 结构所做的事情跟锁(lock)类似:它防止不同的线程同时执行同一段代码。@synchronized是几种iOS多线程同步机制中最慢的一个,同时也是最方便的一个。苹果建立@synchronized的初衷就是方便开发者快速的实现代码同步,语法如下:
@synchronized(object) {
//code
}
Peak大神在文章中提醒我们:
-
慎用@synchronized(self)
synchronized中传入的object的内存地址,被用作key,通过hash map对应的一个系统维护的递归锁。不管是传入什么类型的object,只要是有内存地址,就能启动同步代码块的效果。 -
粒度控制,不同的数据使用不同的锁,尽量将粒度控制在最细的程度
有些人说@synchronized慢,但@synchronized之所以慢是更多的因为没有做好粒度控制。锁本质上是为了让我们的一段代码获得原子性,不同的critical section要使用不同的锁。
@synchronized也经常用来实现单例,用的更多的还是GCD中的一次函数dispatch_once,关于@synchronized和dispatch_once的性能比较,有兴趣的童鞋们可以看这里
2. NS系列锁
NS系列锁指的是NSLock
、NSCondition
、NSConditionLock
、NSRecursiveLock
,之所以把这几个放在一起,是因为它们都遵守NSLocking
协议,就俩方法,加锁解锁,so easy!
@protocol NSLocking
- (void)lock;
- (void)unlock;
@end
2.1 NSLock 线程锁
看下NSLock 的API,嗯,很少也很简单:
@interface NSLock : NSObject <NSLocking> {
@private
void *_priv;
}
- (BOOL)tryLock;
- (BOOL)lockBeforeDate:(NSDate *)limit;
@property (nullable, copy) NSString *name API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
@end
方法说明:
trylock:能加锁返回 YES 并执行加锁操作,相当于 lock,反之返回 NO
lockBeforeDate:这个方法表示会在传入的时间内尝试加锁,若能加锁则执行加锁操作并返回 YES,反之返回 NO。
2.2 NSConditionLock 条件锁
condition:条件,顾名思义NSConditionLock就是有条件的加锁,继续来看API
@interface NSConditionLock : NSObject <NSLocking> {
@private
void *_priv;
}
- (instancetype)initWithCondition:(NSInteger)condition NS_DESIGNATED_INITIALIZER;
@property (readonly) NSInteger condition;
- (void)lockWhenCondition:(NSInteger)condition;
- (BOOL)tryLock;
- (BOOL)tryLockWhenCondition:(NSInteger)condition;
- (void)unlockWithCondition:(NSInteger)condition;
- (BOOL)lockBeforeDate:(NSDate *)limit;
- (BOOL)lockWhenCondition:(NSInteger)condition beforeDate:(NSDate *)limit;
@property (nullable, copy) NSString *name API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
@end
比较了一下,貌似跟NSLock差不多,只是加了个condition,还是NSInteger类型的,感觉只要把这个参数搞明白就差不多了。
condition
我们可以理解为一个条件标示,看下创建方法:initWithCondition:创建的时候传入一个条件标识,之后如果使用创建好的锁,必须传入对应的标识才能完成对应的lock和unlock操作
2.3 NSRecursiveLock 递归锁
@interface NSRecursiveLock : NSObject <NSLocking> {
@private
void *_priv;
}
- (BOOL)tryLock;
- (BOOL)lockBeforeDate:(NSDate *)limit;
@property (nullable, copy) NSString *name API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
@end
2.4 NSCondition 断言
@interface NSCondition : NSObject <NSLocking> {
@private
void *_priv;
}
- (void)wait;
- (BOOL)waitUntilDate:(NSDate *)limit;
- (void)signal;
- (void)broadcast;
@property (nullable, copy) NSString *name API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
@end
3. dispatch_semaphore 信号量实现加锁(GCD)
之前写过一篇dispatch_semaphore信号量的文章,麻烦大家手动移驾过去。
我自己测试的线程锁的性能如下:
image推荐文章(排名不分先后):
(ibireme大神的不再安全的 OSSpinLock)
https://www.jianshu.com/p/1e59f0970bf5
关于递归锁与非递归锁,平常接触比较少,有兴趣的童鞋可以了解一下:https://blog.csdn.net/zouxinfox/article/details/5838861
网友评论