title: Is -allObjects on NSMutableSet thread safe?
body: <p>The documentation on NSSet objectEnumeration <a href="https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSSet_Class/Reference/Reference.html#//apple_ref/occ/instm/NSSet/objectEnumerator" rel="nofollow">says</a>:</p>
  
  <blockquote>
    <p>When this method is used with mutable subclasses of NSSet, your code shouldn’t modify the set during enumeration. If you intend to modify the set, use the allObjects method to create a “snapshot” of the set’s members. Enumerate the snapshot, but make your modifications to the original set.</p>
  </blockquote>
  
  <p>Now my question is: <strong>Is the allObjects method itself thread safe?</strong></p>
  
  <p>I have implemented an operation set like so:</p>
  
  <pre><code>@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
  </code></pre>
  
  <p>This all works fine.
  But I have got a crash via Crashlytics, which is rare (two out of hundreds), but is there:</p>
  
  <pre><code>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
  </code></pre>
  
  <p>The OperationSet is accessed from multiple threads.</p>
  
  <p>Any help is greatly appreciated. </p>
  
  <p><strong>EDIT</strong></p>
  
  <p>Thanks dasblinkenlight for enlighting the allObjects usage.
  I have edited my implementation like so:</p>
  
  <pre><code>@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
  </code></pre>
  
  <p>The code works! Which is a good sign, but can you please review it?</p>
  
  <p>And there is another question: <strong>Is there any difference in using allObjects versus making a set copy?</strong></p>
  
  <p>That is using this code:</p>
  
  <pre><code>- (void)enumerateWithOperationBlock:(OperationBlock)block
  {
      __block NSArray *allObjects;
      dispatch_sync(_queue, ^{
          allObjects = [_set allObjects];
      });
      [allObjects enumerateObjectsUsingBlock:^(Operation *o, NSUInteger idx, BOOL *stop) {
          block(o);
      }];
  }
  </code></pre>
  
  <p>over this code:</p>
  
  <pre><code>- (void)enumerateWithOperationBlock:(OperationBlock)block
  {
      __block NSSet *safeSet;
      dispatch_sync(_queue, ^{
          safeSet = [_set copy];
      });
      [safeSet enumerateObjectsUsingBlock:^(Operation *o, BOOL *stop) {
          block(o);
      }];
  }
  </code></pre>
  
  <p>Thanks for your help.</p>