在Objective-C中如果多个线程访问同一个代码,有可能会出现问题。这种情况下需要使用锁来实现同步机制。�
同步锁常用的有1、@synchronized 2、NSLock 3、GCD派发队列
一、@synchronized使用方法为
TestObjectA.h
@interface TestObjectA : NSObject
@property (nonatomic, copy) NSString *baseString;
@end
TestObjectA.m
@synchronized(self){
_baseString = baseString;
}
可以避免_baseString被重复的写入。
但是这种方法解决了多个线程访问同一资源的时候的问题。但是也有一些缺陷。
1.会影响执行代码的性能
2.在项目中有多处@synchronized时,会比较危险。因为所有的同步锁都在彼此争夺同一个锁。要是每个属性都这么写。那么每个属性的同步块,都要等到别的同步块完成的时候才能正常执行。
二。NSLock
使用方法如下
NSLock *baseLock = [[NSLock alloc]init];
[baseLock lock];
TestObjectA *objectA = [[TestObjectA alloc]init];
objectA.baseString = @"testBaseString";
NSLog(@"%@",objectA.baseString);
[baseLock unlock];
但是这些方法并不一定是绝对线程安全的,因为在一个线程访问多次访问这段代码的过程中间。有可能获取的值并不相同。因为在两次获取的中间过程中。值可能会被其他线程所修改。
三、GCD(派发队列)
此模式的思路是将获取属性和设置属性的代码安排到序列化的队列上去。这样针对属性的所有操作就都同步了。
使用如下
TestObjectA.h
#import <Foundation/Foundation.h>
@interface TestObjectA : NSObject
@property (nonatomic, copy) NSString *baseString;
@end
TestObjectA.m
#import "TestObjectA.h"
@interface TestObjectA ()
@property (nonatomic, strong) dispatch_queue_t baseDispatch;
@end
@implementation TestObjectA
@synthesize baseString = _baseString;
- (instancetype)init{
if (self = [super init]){
_baseDispatch = dispatch_queue_create("baseQueue", NULL);
}
return self;
}
- (NSString *)baseString{
__block NSString *cacheString;
dispatch_sync(_baseDispatch, ^{
cacheString = _baseString;
});
return cacheString;
}
- (void)setBaseString:(NSString *)baseString{
dispatch_sync(_baseDispatch, ^{
_baseString = baseString;
});
}
此方法通过创建一个串行队列。然后同步执行baseString属性的get和set方法。可以保证设置属性和获取属性的时候属性值唯一执行。不会产生冲突。
优化1
我们还可以对此方法的set方法进行优化、因为设置方法不一定非得是同步的我们可以将set方法进行异步执行。
修改后TestObjectA.m中代码如下
- (instancetype)init{
if (self = [super init]){
_baseDispatch = dispatch_queue_create("baseQueue", NULL);
}
return self;
}
- (NSString *)baseString{
__block NSString *cacheString;
dispatch_sync(_baseDispatch, ^{
cacheString = _baseString;
});
return cacheString;
}
- (void)setBaseString:(NSString *)baseString{
dispatch_async(_baseDispatch, ^{
_baseString = baseString;
});
}
优化2
进行属性获取的时候理论上应该可以做到多个获取方法同时执行。所以可以使用并发队列。在获取属性的时候提高效率进行此优化需要用到一个GCD功能--栅栏
TestObjectA.m的代码如下
代码如下
#import "TestObjectA.h"
@interface TestObjectA ()
@property (nonatomic, strong) dispatch_queue_t baseDispatch;
@end
@implementation TestObjectA
@synthesize baseString = _baseString;
- (instancetype)init{
if (self = [super init]){
_baseDispatch = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
}
return self;
}
- (NSString *)baseString{
__block NSString *cacheString;
dispatch_sync(_baseDispatch, ^{
cacheString = _baseString;
});
return cacheString;
}
- (void)setBaseString:(NSString *)baseString{
dispatch_barrier_async(_baseDispatch, ^{
_baseString = baseString;
});
}
1、使用并发队列在进行属性的获取的时候。可以同时开启多个线程来获取属性
2、使用栅栏函数后读取操作仍然并发执行。但是进行设置方法的时候会等进行读取操作的队列完成之后再执行set方法。可以保证进行读取方法的时候不会修改属性的值。保证了线程安全性。同时又提高了性能。