title: Why does this KVO code crash 100% of the time?
body: <p>The code below will crash inside of <code>NSKVOUnionSetAndNotify</code> calling <code>CFDictionaryGetValue</code> with what appears to be a bogus dictionary.</p>
  
  <p>It seems to be a race between the swizzled <code>addFoos</code> / <code>NSKVOUnionSetAndNotify</code> code and the act of adding and removing KVO observers.</p>
  
  <pre class="lang-objective-c prettyprint-override"><code>#import &lt;Foundation/Foundation.h&gt;
  @interface TestObject : NSObject
  @property (readonly) NSSet *foos;
  @end
  
  @implementation TestObject {
      NSMutableSet *_internalFoos;
      dispatch_queue_t queue;
      BOOL observed;
  }
  
  - (id)init {
      self = [super init];
      _internalFoos = [NSMutableSet set];
      queue = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);
      return self;
  }
  
  - (void)start {
      // Start a bunch of work hitting the unordered collection mutator
      for (int i = 0; i &lt; 10; i++) {
          dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
              while (YES) {
                  @autoreleasepool {
                      [self addFoos:[NSSet setWithObject:@(rand() % 100)]];
                  }
              }
          });
      }
  
      // Start work that will constantly observe and unobserve the unordered collection
      [self observe];
  }
  
  - (void)observe {
      dispatch_async(dispatch_get_main_queue(), ^{
          observed = YES;
          [self addObserver:self forKeyPath:@"foos" options:0 context:NULL];
      });
  }
  
  - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
      dispatch_async(dispatch_get_main_queue(), ^{
          if (observed) {
              observed = NO;
              [self removeObserver:self forKeyPath:@"foos"];
              [self observe];
          }
      });
  }
  
  // Public unordered collection property
  - (NSSet *)foos {
      __block NSSet *result;
      dispatch_sync(queue, ^{
          result = [_internalFoos copy];
      });
      return result;
  }
  
  // KVO compliant mutators for unordered collection
  - (void)addFoos:(NSSet *)objects {
      dispatch_barrier_sync(queue, ^{
          [_internalFoos unionSet:objects];
      });
  }
  
  - (void)removeFoos:(NSSet *)objects {
      dispatch_barrier_sync(queue, ^{
          [_internalFoos minusSet:objects];
      });
  }
  @end
  
  int main(int argc, const char * argv[]) {
      @autoreleasepool {
          TestObject *t = [[TestObject alloc] init];
          [t start];
          CFRunLoopRunInMode(kCFRunLoopDefaultMode, 10000, false);
      }
      return 0;
  }
  </code></pre>