NSMutableArray是線程不安全的,當有多個線程同時對數組進行操作的時候可能導致崩潰或數據錯誤
線程鎖:使用線程鎖對數組讀寫時進行加鎖
派發(fā)隊列:在《Effective Objective-C 2.0..》書中第41條:多用派發(fā)隊列,少用同步鎖中指出:使用“串行同步隊列”(serial synchronization queue),將讀取操作及寫入操作都安排在同一個隊列里,即可保證數據同步。而通過并發(fā)隊列,結合GCD的柵欄塊(barrier)來不僅實現數據同步線程安全,還比串行同步隊列方式更高效。
方案二
iOS-SDK只提供了非線程安全的數組。如果要多線程并發(fā)的使用一個數組對象就必須要加鎖,頻繁的加鎖使得代碼的調用非常的麻煩。
我們需要多線程的讀寫鎖在類的內部實現,所以需要對NSMutableArray進行封裝,封裝后的對象負責接受所有事件并將其轉發(fā)給真正的NSMutableArray對象,并通過合理的調度使得其支持多線程并發(fā)。
1 新建一個對象來對NSMutableArray 數組進行封裝,包含
dispatch_queue_t 調度隊列對象 和一個
NSObject 具體操作對象作為成員變量
并且初始化操作對象
- (id)init {
self = [super init];
if (self) {
self.container = [NSMutableArray array];
}
return self;
}
2.回到JXMultiThreadObject類中 利用下面方法對一個對象無法實現的方法進行攔截和派發(fā)
- (void)forwardInvocation:(NSInvocation *)anInvocation {
}
該方法當你調用了一個對象沒有實現的方法時,forwardInvocation方法將會響應,并讓你覺得這個方法的處理方式,
在這之前你需要先實現
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
return [[_container class] instanceMethodSignatureForSelector:aSelector];
}
才能激活forwardInvocation
3.這里利用到G-C-D的調度機制對對象和對象的行為進行調度
- (void)forwardInvocation:(NSInvocation *)anInvocation {
dispatch_barrier_sync(_dispatchQueue, ^{
[anInvocation invokeWithTarget:_container];
});
}
這里使用了同步的阻塞調度,屬于效率比較低的一種調度方式,可以簡單地作一下優(yōu)化
- (void)forwardInvocation:(NSInvocation *)anInvocation {
NSMethodSignature *sig = [anInvocation valueForKey:@"_signature"];
const char *returnType = sig.methodReturnType;
if (!strcmp(returnType, "v")) {
dispatch_barrier_async(_dispatchQueue, ^{
[anInvocation invokeWithTarget:_container];
});
}
else {
dispatch_barrier_sync(_dispatchQueue, ^{
[anInvocation invokeWithTarget:_container];
});
}
}
獲取調度方法的返回值,如果是void型方法則使用異步調度,如果是getter類型的則使用同步調度,可以略微的提升性能。
你可以通過繼承等方法為不同類型的container指定不同的調度規(guī)則以確保在邏輯正常的情況下擁有最高的性能。