Search code examples
iosobjective-ckey-value-observingkvc

mutableArrayValueForKey NSMutableArray crash when removing objects


I'm running into a crash with the following code. self.items is an NSArray of NSManagedObjects with an "id" attribute of type String:

NSMutableArray *allIDs = [self.items mutableArrayValueForKey:@"id"];
NSArray *presentIDs = // a subset of allIDs
[allIDs removeObjectsInArray:presentIDs];

Here is the crash message:

'Unacceptable type of value for attribute: property = "id"; desired type = NSString; given type = __NSArrayM; value = ....

Printing it out in debugger reveals that allID is a NSKeyValueSlowMutableArray instead of __NSArrayM for a regular NSMutableArray.

So I changed it to this and it works:

NSMutableArray *allIDs = [[self.items valueForKey:@"id"] mutableCopy];

What is the difference between these two calls that would make the former crash but the latter work? I suspect it's something to do with KVO and proxy collections but don't quite understand it.


Solution

  • I suspect it's something to do with KVO and proxy collections but don't quite understand it.

    Right, though not KVO, KVC (Key-Value Coding).

    From the NSKeyValueCoding Protocol Reference

    mutableArrayValueForKey:

    Returns a mutable array proxy that provides read-write access to an ordered to-many relationship specified by a given key.

    In other words, the purpose of mutableArrayValueForKey: is to provide mutable access to an immutable array stored in a property of the receiver.

    On the other hand, KVC provides a special implementation of valueForKey: for properties of type NSArray that causes the target array to propagate the valueForKey: message to each of its elements, and return an array of all the resulting values.

    So in your second example, sending valueForKey: to the items array resulted in an array of values obtained by sending valueForKey:@"id" to each item in the array. The first example would only work if the items array itself had a property named id of type NSArray *.