Take the 2-minute tour ×

I have a class that contains an NSSet. That object is called _collectibles, and in a method, I make a copy of that set in order to do some processing, something like:

NSSet* collectibleCopy = [_collectibles copy];

In practice, I see this regularly crash with this message:

[__NSPlaceholderSet initWithObjects:count:]: attempt to insert nil object from objects

I've resolved the issue by changing the above code to:

NSMutableSet* collectibleCopy = [[NSMutableSet alloc] initWithCapacity: [_collectibles count]];
for ( id thing in _collectibles ) {
    [collectibleCopy addObject: thing];

And now I can no longer reproduce any such crash. I'm betting [copy] is more efficient, and I'd rather use it, but I can't figure out why it's completely wonky!

Update: while full context would take a ton of explanation, the keys to me solving this were that, a, the code was invoked this way:

NSBlockOperation* operation = [NSBlockOperation blockOperationWithBlock: ^{
   [thing doStuff];

[operationQueue addOperation: operation];

And that I was, basically by making a bunch of things slower, catch the app with 2 threads running 2 threads for a queue initialized thusly:

operationQueue.maxConcurrentOperationCount = 1;

Which I thought impossible. The clue was that the second thread was in [NSAutoreleasePool drain], which led me to learn that NSOperationQueue can do autorelease stuff whenever/however it wants.

share|improve this question
I wasn't able to reproduce your bug. Could you post a bit more code for context? –  aLevelOfIndirection May 24 '13 at 14:07
is the set that you are copying a core data relationship? –  Grady Player May 24 '13 at 14:26
The entire context would require a huge pile of code. :) –  GoldenBoy May 24 '13 at 16:02
add comment

2 Answers


NSSet* collectibleCopy = [NSSet setWithSet:_collectibles] 

work for you?

share|improve this answer
It might have- I'm guessing the reason it didn't crash (see my other answer) was something about the semantics of fast iteration vs. whatever NSSet does for the NSCopying protocol. –  GoldenBoy May 24 '13 at 16:01
add comment

OK, so huzzah for having actually figured this out.

The trick here was that this operation was performed on an async NSOperationQueue. TIL that NSOperationQueues have AutoreleasePools, but that they get drained at the discretion of GCD. In this case, the pool from a previous operation was being drained on another thread concurrently, causing what amounts to a rather opaque concurrent modification problem.


@autoreleasepool inside the block on which this code got invoked. This causes the drain to happen as part of the block, rather than asynchronously, and my race condition goes away.

share|improve this answer
add comment

Your Answer


