I made a mistake while creating a TableView class
, and accidentally kept my @property
as copy
when I defined it:
@property (copy, nonatomic) NSMutableArray *words;
I initialised the array "correctly": (note this is the third attempt, so please ignore the fact that I'm not using mutableCopy and other better ways of doing this)
NSArray *fixedWords = @[@"Eeny", @"Meeny", @"Miny", @"Moe", @"Catch", @"A", @"Tiger", @"By", @"His", @"Toe"];
NSMutableArray *mutWords = [[NSMutableArray alloc] initWithArray:fixedWords];
self.words = mutWords;
However when I later came to reorder the array, it crashed on the removeObjectAtIndex line:
id object = [self.words objectAtIndex:fromIndexPath.row];
NSUInteger from = fromIndexPath.row;
NSUInteger to = toIndexPath.row;
[self.words removeObjectAtIndex:from];
With the error message
unrecognized selector sent to instance
Took a lot of digging to figure out that this is because the copy means that assigning the NSMutableArray results in creation of a standard (nonmutable) NSArray. Can anyone explain why this is the correct behaviour?
-copy, as implemented by mutable Cocoa classes, always returns their immutable counterparts. Thus, when an NSMutableArray is sent -copy, it returns an NSArray containing the same objects.
Because words
has the memory qualifier copy
, this line:
NSMutableArray *mutWords = [[NSMutableArray alloc] initWithArray:fixedWords];
self.words = mutWords;
Expands out to:
NSMutableArray *mutWords = [[NSMutableArray alloc] initWithArray:fixedWords];
self.words = [mutWords copy];
Given that NSMutableArray is a subclass of NSArray, the compiler doesn't complain, and you now have a ticking time bomb on your hands because NSArray does not recognize it's mutable subclass' methods (because it cannot mutate it's contents).