Unusual NSSet Copy

I have a class containing an NSSet . This object is called _collectibles , and in the method I make a copy of this set to do some processing, for example:

 NSSet* collectibleCopy = [_collectibles copy]; 

In practice, I see that this regularly crashes with this message:

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

I solved the problem by changing the code above:

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

And now I can no longer reproduce such a collapse. I am sure that [copy] more efficient, and I would prefer to use it, but I can’t understand why it wins completely!

Update: while the full context will require tons of explanations, the keys I solved were the following: a, the code was called as follows:

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

And I was basically making a bunch of things slower, caught an application with two threads that started 2 threads for a queue initialized as follows:

 operationQueue.maxConcurrentOperationCount = 1; 

What I thought was impossible. The hint was that the second thread was in [NSAutoreleasePool drain], which made me find out that NSOperationQueue can do autorelease stuff whenever it wants.

+7
source share
2 answers

Will be

 NSSet* collectibleCopy = [NSSet setWithSet:_collectibles] 

works for you?

+2
source

Ok, so huzzah for really understanding this.

The trick here was that this operation was performed using async NSOperationQueue . TILs that NSOperationQueues have AutoreleasePools, but that they are depleted at the discretion of the GCD. In this case, the pool from the previous operation simultaneously merges with another thread, which leads to a rather opaque problem of simultaneous modification.

Decision:

@autoreleasepool inside the block this code was called on. This causes the leak to occur as part of the block, and not asynchronously, and my race condition disappears.

+2
source

All Articles