2016-09-28 77 views
1

我寫的類SafeMutableDictionary繼承NSMutableDictionary。
類實現唯一的 「原始」 的方法,其must be inherits
從的NSDictionary:線程安全NSMutableDictionary

- (instancetype)init; 
- (instancetype)initWithObjects:(const id [])objects forKeys:(const id<NSCopying>[])keys count:(NSUInteger)cnt; 
- (NSUInteger)count; 
- (id)objectForKey:(id)key; 
- (NSEnumerator*)keyEnumerator; 

和從的NSMutableDictionary:

- (void)removeObjectForKey:(id)key; 
- (void)setObject:(id)obj forKey:(id)key; 

線程安全性支持通過使用的NSMutableDictionary類型的內可變,這保存所有數據

@interface SafeMutableDictionary() { 
    __strong NSMutableDictionary* _dictEmbedded; 
} 

和每個訪問它包裝@synchronized

- (id)objectForKey:(id)key{ 
    @synchronized (_dictEmbedded) { 
     return [_dictEmbedded objectForKey:key]; 
    } 
} 

github的全碼回覆。

但不幸的是,我仍然得到崩潰,像

Collection <__NSDictionaryM: 0x16784ff0> was mutated while being enumerated. 

所以錯誤,我有一些問題:
1)我的實現是正確的?我錯過了什麼?
2)是否存在更多着名和經過測試的解決方案?
3)從main和bg線程同時訪問容器的最佳實踐是什麼?
可能是做這樣的繼承最糟糕的做法,更好地利用原來的容器+護理線程安全的

+0

忘掉'@ synchronized'。只需在串行隊列中處理你的字典。 –

回答

1

有兩個獨立的要求,在這裏:

  1. 多線程安全,即集合(或您的案例中的字典)在多個線程同時訪問時保持一致。

  2. 當集合被同時修改(例如枚舉集合並移除枚舉器返回的選定元素)時,魯棒性即迭代器(有時稱爲枚舉器)保持一致。即使在單線程環境中,這仍然是一個挑戰。

你的解決方案解決了要求1.但你得到的錯誤信息大約需要2 NSDictionary情況下是不穩健。很少的集合推理(以任何編程語言)都是。

+0

謝謝你的解釋。畫面變得更加清晰。 –

0

@synchronized確保您不能從多個線程同時訪問一個對象,但不能保證在情況出現時一切都會好的。

我所做的解決,這是創建一個基類NBSharedObjectWithLock:隨着thise類 ,您可以:

myObjetc = [[MyClassInheritingFromNBSharedObjectWithLoc] alloc] initWithMutexMode:PTHREAD_MUTEX_RECURSIVE]; 

[myObject lock]; // when you need exclusive access to the object 
[myObject unlock]; // when exclusive access to the object is no more needed 

NBSharedObjectWithLoc.h

// 
// NBSharedObjectWithLock.h 
// NBFoundation 
// 
// Created by Nicolas Buquet on 03/06/2016. 
// Copyright © 2016 Nicolas Buquet. All rights reserved. 
// 

#import <Foundation/Foundation.h> 
#include <pthread.h> 

@interface NBSharedObjectWithLock : NSObject 

- (instancetype)initWithMutexMode:(int)mutexMode; 
// mutexMode can be: 
// - PTHREAD_MUTEX_NORMAL 
// - PTHREAD_MUTEX_ERRORCHECK 
// - PTHREAD_MUTEX_RECURSIVE 
// - PTHREAD_MUTEX_DEFAULT (= PTHREAD_MUTEX_NORMAL) 

- (BOOL)lock; 
- (BOOL)unlock; 

@end 

NBSharedObjectWithLoc.m

// 
// NBSharedObjectWithLock.m 
// NBFoundation 
// 
// Created by Nicolas Buquet on 03/06/2016. 
// Copyright © 2016 Nicolas Buquet. All rights reserved. 
// 

#import "NBSharedObjectWithLock.h" 

@implementation NBSharedObjectWithLock 
{ 
    pthread_mutex_t _mutexLock; 
} 

- (instancetype)initWithMutexMode:(int)mutexMode 
{ 
    // mutexMode can be: 
    // - PTHREAD_MUTEX_NORMAL 
    // - PTHREAD_MUTEX_ERRORCHECK 
    // - PTHREAD_MUTEX_RECURSIVE 
    // - PTHREAD_MUTEX_DEFAULT (= PTHREAD_MUTEX_NORMAL) 

    self = [super init]; 

    if (!self) 
     return nil; 

    pthread_mutexattr_t mutexAttributes; 

    pthread_mutexattr_init(&mutexAttributes); 
    pthread_mutexattr_settype(&mutexAttributes, mutexMode); 
    pthread_mutex_init(&_mutexLock, &mutexAttributes); 

    return self; 
} 

- (void)dealloc 
{ 
    pthread_mutex_destroy(&_mutexLock); 

    [super dealloc]; 
} 

- (BOOL)lock 
{ 
    return pthread_mutex_lock(&_mutexLock); 
} 

- (BOOL)unlock 
{ 
    return pthread_mutex_unlock(&_mutexLock); 
} 

@end 

使用這些基類,可以從一個線程訪問一個對象。鎖將被放在它上面。如果另一個線程想訪問它,它將等待(該線程將被暫停),直到該對象的鎖被釋放。如果同一線程上的多個對象鎖定在同一線程上,則可以訪問鎖定的對象。

鎖定/解鎖調用替換@synchronize塊。 請注意,您不得在鎖定呼叫和解鎖呼叫之間返回,否則您的對象將永遠無法解鎖。如果你需要返回一個值:

- (NSDictionary *)dictionaryFromLockedObject 
{ 
    [myObject lock] 
    NSDictionary *dict = [myObject callAMethodThatReturnsADictionary]; 
    [myObject unlock] 

    return dict; 
}