I made a mistake while creating a TableView class
, and accidentally kept my @property
as copy
when I defined it:
@property
-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).
Properties aren't magical, they're just shorthand. Declaring a @property
on your object tells the compiler to create a backing instance variable and accessor methods for it. The actual generated code depends on the attributes you set on your property.
It's important to remember that setting a property using dot syntax is also shorthand. When you call…
self.words = mutWords;
…you're actually invoking the generated accessor method behind the scenes, like this:
[self setWords:mutWords];
Since you specified the copy
attribute on your property, you've told the compiler to generate that -setWords:
accessor method with code that looks something like this:
- (void)setWords:(NSMutableArray *)words
{
_words = [words copy];
}
Knowing all that, you can see what's happening: the generated setter method will call -copy
on the input argument, and assign the result to the backing instance variable. Because the -copy
method is always implemented to return a non-mutable object (performing [aMutableString copy]
will return an NSString, and so on) setting that property will always store a non-mutable copy.