Objective c NSMutableSet上的-AllObject是线程安全的吗?

Objective c NSMutableSet上的-AllObject是线程安全的吗?,objective-c,multithreading,thread-safety,nsmutableset,Objective C,Multithreading,Thread Safety,Nsmutableset,有关NSSet objectEnumeration的文档: 当此方法用于NSSet的可变子类时,代码不应在枚举期间修改该集合。如果要修改集合,请使用AllObject方法创建集合成员的“快照”。枚举快照,但对原始集进行修改 现在我的问题是:allObjects方法本身是线程安全的吗? 我实现了如下操作集: @interface OperationSet : NSObject @end @implementation OperationSet { NSMutableSet *_set; }

有关NSSet objectEnumeration的文档:

当此方法用于NSSet的可变子类时,代码不应在枚举期间修改该集合。如果要修改集合,请使用AllObject方法创建集合成员的“快照”。枚举快照,但对原始集进行修改

现在我的问题是:allObjects方法本身是线程安全的吗?

我实现了如下操作集:

@interface OperationSet : NSObject
@end
@implementation OperationSet
{
    NSMutableSet *_set;
}
- (instancetype)init
{
    self = [super init];
    if (self)
    {
        _set = [[NSMutableSet alloc] init];
    }
    return self;
}
- (void)addOperation:(Operation *)operation
{
    if (operation)
    {
        [_set addObject:operation];
    }
}
- (void)removeOperation:(Operation *)operation
{
    if (operation)
    {
        [_set removeObject:operation];
    }
}
- (void)removeAllOperations
{
    [_set removeAllObjects];
}
- (void)enumerateWithOperationBlock:(OperationBlock)block
{
    NSArray *allObjects = [_set allObjects];
    [allObjects enumerateObjectsUsingBlock:^(Operation *o, NSUInteger idx, BOOL *stop) {
        block(o);
    }];
}
- (void)flushCompletedOperations
{
    NSArray *allObjects = [_set allObjects];
    NSSet *safeSet = [NSSet setWithArray:allObjects];
    NSSet *completed = [safeSet objectsPassingTest:^BOOL(Operation *o, BOOL *stop){
        return o.completed;
    }];
    [_set minusSet:completed];
}
- (NSUInteger)count
{
    return [_set count];
}
- (BOOL)any:(OperationAnyBlock)block
{
    NSArray *allObjects = [_set allObjects];
    NSUInteger index = [allObjects indexOfObjectPassingTest:^BOOL(Operation *o, NSUInteger idx, BOOL *stop) {
        return block(o);
    }];
    return (index != NSNotFound);
}
- (Operation *)getOperationWithMatchingData:(NSDictionary *)data
{
    NSArray *allObjects = [_set allObjects];
    NSUInteger index = [allObjects indexOfObjectPassingTest:^BOOL(Operation *o, NSUInteger idx, BOOL *stop) {
        return [o matchesData:data];
    }];
    return (index == NSNotFound ? nil : allObjects[index]);
}
@end
这一切都很好。 但我已经通过Crashlytics获得了一次崩溃,这是罕见的(百分之二),但是否存在:

EXC_BAD_ACCESS KERN_INVALID_ADDRESS at 0x0000000000000008
Thread : Crashed: com.apple.main-thread
0  CoreFoundation                 0x000000018772c438 -[__NSSetM addObject:] + 448
1  CoreFoundation                 0x000000018772c430 -[__NSSetM addObject:] + 440
操作集可从多个线程访问

非常感谢您的帮助

编辑

感谢dasblinkenlight为AllObject的使用提供了启示。 我对我的实现进行了如下编辑:

@interface OperationSet : NSObject
@end
@implementation OperationSet
{
    NSMutableSet *_set;
    dispatch_queue_t _queue;
}
- (instancetype)init
{
    self = [super init];
    if (self)
    {
        _set = [[NSMutableSet alloc] init];
        _queue = dispatch_queue_create("OperationQueue", DISPATCH_QUEUE_SERIAL);
    }
    return self;
}
- (void)addOperation:(Operation *)operation
{
    if (operation)
    {
        dispatch_async(_queue, ^{
            [_set addObject:operation];
        });
    }
}
- (void)removeOperation:(Operation *)operation
{
    if (operation)
    {
        dispatch_async(_queue, ^{
            [_set removeObject:operation];
        });
    }
}
- (void)removeAllOperations
{
    dispatch_async(_queue, ^{
        [_set removeAllObjects];
    });
}
- (void)enumerateWithOperationBlock:(OperationBlock)block
{
    __block NSArray *allObjects;
    dispatch_sync(_queue, ^{
        allObjects = [_set allObjects];
    });
    [allObjects enumerateObjectsUsingBlock:^(Operation *o, NSUInteger idx, BOOL *stop) {
        block(o);
    }];
}
- (void)flushCompletedOperations
{
    __block NSArray *allObjects;
    dispatch_sync(_queue, ^{
        allObjects = [_set allObjects];
    });
    NSSet *safeSet = [NSSet setWithArray:allObjects];
    NSSet *completed = [safeSet objectsPassingTest:^BOOL(Operation *o, BOOL *stop){
        return o.completed;
    }];
    [_set minusSet:completed];
}
- (NSUInteger)count
{
    return [_set count];
}
- (BOOL)any:(OperationAnyBlock)block
{
    __block NSArray *allObjects;
    dispatch_sync(_queue, ^{
        allObjects = [_set allObjects];
    });
    NSUInteger index = [allObjects indexOfObjectPassingTest:^BOOL(Operation *o, NSUInteger idx, BOOL *stop) {
        return block(o);
    }];
    return (index != NSNotFound);
}
- (Operation *)getOperationWithMatchingData:(NSDictionary *)data
{
    __block NSArray *allObjects;
    dispatch_sync(_queue, ^{
        allObjects = [_set allObjects];
    });
    NSUInteger index = [allObjects indexOfObjectPassingTest:^BOOL(Operation *o, NSUInteger idx, BOOL *stop) {
        return [o matchesData:data];
    }];
    return (index == NSNotFound ? nil : allObjects[index]);
}
@end
代码有效!这是个好兆头,但你能回顾一下吗

还有另一个问题:使用allobject和创建一个set拷贝有什么区别吗?

即使用此代码:

- (void)enumerateWithOperationBlock:(OperationBlock)block
{
    __block NSArray *allObjects;
    dispatch_sync(_queue, ^{
        allObjects = [_set allObjects];
    });
    [allObjects enumerateObjectsUsingBlock:^(Operation *o, NSUInteger idx, BOOL *stop) {
        block(o);
    }];
}
- (void)enumerateWithOperationBlock:(OperationBlock)block
{
    __block NSSet *safeSet;
    dispatch_sync(_queue, ^{
        safeSet = [_set copy];
    });
    [safeSet enumerateObjectsUsingBlock:^(Operation *o, BOOL *stop) {
        block(o);
    }];
}
关于此代码:

- (void)enumerateWithOperationBlock:(OperationBlock)block
{
    __block NSArray *allObjects;
    dispatch_sync(_queue, ^{
        allObjects = [_set allObjects];
    });
    [allObjects enumerateObjectsUsingBlock:^(Operation *o, NSUInteger idx, BOOL *stop) {
        block(o);
    }];
}
- (void)enumerateWithOperationBlock:(OperationBlock)block
{
    __block NSSet *safeSet;
    dispatch_sync(_queue, ^{
        safeSet = [_set copy];
    });
    [safeSet enumerateObjectsUsingBlock:^(Operation *o, BOOL *stop) {
        block(o);
    }];
}

感谢您的帮助。

NSMutableSet
不是线程安全的。如果希望从多个线程访问一个线程,则必须自己强制一次访问一个线程

这在中有记录


强制一次一个访问的典型方法是创建一个GCD队列(针对每个集合)并仅从该队列访问集合(使用
dispatch\u sync
,或者如果可能,使用
dispatch\u async
)。在您的示例中,您将向类中添加一个
dispatch\u queue\t
实例变量,在
init
中初始化它,并在其他每个实例方法中使用它。

NSMutableSet
不是线程安全的。如果希望从多个线程访问一个线程,则必须自己强制一次访问一个线程

这在中有记录


强制一次一个访问的典型方法是创建一个GCD队列(针对每个集合)并仅从该队列访问集合(使用
dispatch\u sync
,或者如果可能,使用
dispatch\u async
)。在您的示例中,您将向类中添加一个
dispatch\u queue\t
实例变量,在
init
中初始化它,并在其他每个实例方法中使用它。

NSMutableSet
是,因此除非另有明确说明,否则其方法应被视为非线程安全的(目前没有一个
NSMutableSet
方法被记录为线程安全的)

我认为

使用
allObjects
方法创建“快照”


他们的意思是在锁后创建快照,以避免在枚举其对象并对其执行操作的整个时间内对整个集合持有锁。

NSMutableSet
是,因此除非另有明确说明,否则其方法应视为非线程安全的(目前没有一个
NSMutableSet
方法被记录为线程安全的)

我认为

使用
allObjects
方法创建“快照”


他们的意思是在锁后面创建一个快照,以避免在枚举对象并对其执行操作的整个过程中对整个集合持有锁。

您的另一个问题:[mySet allObjects]返回一个包含集合中所有对象的NSArray,而[mySet copy]返回NSSet。如果您不需要集合的属性(非常快速的成员资格测试),NSArray可能会更快一些。

您的另一个问题:[mySet allObjects]返回包含集合中所有对象的NSArray,而[mySet copy]返回NSSet。如果您不需要集合的属性(非常快速的会员测试),NSArray可能会快一点。

谢谢您的回答。请查看我编辑的问题。Cheers@rob-mayoff我有一个类似的问题,我还没有找到实现GCD队列的方法(我现在正在使用锁)我有一个类,它是一个C++向量的包装器。它有添加元素的方法(推到向量上)-很容易排队。但是,我也需要在背景线程中的另一个类中的一些数学重的方法中直接访问向量指针(想想TIC TOC写/读)。.现在我的解决方案是让指针getter锁定该类(因此新数据被添加到临时数组)完成后,数学重的方法调用unlock。我将如何在此处使用队列?@Adam你应该发布一个新问题,而不是对我的答案发表评论。@robmayoff done:谢谢你的回答。请查看我编辑的问题。Cheers@rob-mayoff我有一个类似的问题,我还没有找到实现GCD队列的方法(我现在正在使用锁)我有一个类,它是一个C++向量的包装器。它有添加元素的方法(推到向量上)-很容易排队。但是,我也需要在背景线程中的另一个类中的一些数学重的方法中直接访问向量指针(想想TIC TOC写/读)。。现在我的解决方案是让指针getter锁定类(以便将新数据添加到临时数组中),并在完成时解锁数学密集型方法调用。我将如何在此处使用队列?@Adam您应该发布一个新问题,而不是对我的答案发表评论。@robmayoff完成: