Why does a (copy, nonatomic) NSMutableArray property create NSArrays?

时间秒杀一切 提交于 2019-12-03 03:14:03

问题


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?


回答1:


-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).




回答2:


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.



来源:https://stackoverflow.com/questions/14856681/why-does-a-copy-nonatomic-nsmutablearray-property-create-nsarrays

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!