23

我在创建 TableView 时犯了一个错误class,并且不小心保留了我@property定义copy它时的样子:

@property (copy, nonatomic) NSMutableArray *words; 

我“正确”地初始化了数组:(注意这是第三次尝试,所以请忽略我没有使用 mutableCopy 和其他更好的方法的事实)

NSArray *fixedWords = @[@"Eeny", @"Meeny", @"Miny", @"Moe", @"Catch", @"A", @"Tiger", @"By", @"His", @"Toe"];
NSMutableArray *mutWords = [[NSMutableArray alloc] initWithArray:fixedWords];
self.words = mutWords;

但是,当我后来重新排序数组时,它在 removeObjectAtIndex 行上崩溃了:

id object = [self.words objectAtIndex:fromIndexPath.row];
NSUInteger from = fromIndexPath.row;
NSUInteger to = toIndexPath.row;
[self.words removeObjectAtIndex:from];

带有错误消息

unrecognized selector sent to instance

花了很多时间才弄清楚这是因为副本意味着分配 NSMutableArray 会导致创建标准(非可变)NSArray。谁能解释为什么这是正确的行为?

4

2 回答 2

36

-copy,由可变的 Cocoa 类实现,总是返回它们的不可变副本。因此,当一个 NSMutableArray 被发送 -copy 时,它返回一个包含相同对象的 NSArray。

因为words有内存限定符copy,所以这一行:

NSMutableArray *mutWords = [[NSMutableArray alloc] initWithArray:fixedWords];
self.words = mutWords;

扩展到:

NSMutableArray *mutWords = [[NSMutableArray alloc] initWithArray:fixedWords];
self.words = [mutWords copy];

鉴于 NSMutableArray 是 NSArray 的子类,编译器不会抱怨,而且您现在手上有一颗定时炸弹,因为 NSArray 无法识别它是可变子类的方法(因为它不能改变它的内容)。

于 2013-02-13T15:19:47.900 回答
8

属性并不神奇,它们只是简写。在您的对象上声明 a@property会告诉编译器为其创建一个支持实例变量和访问器方法。实际生成的代码取决于您在属性上设置的属性。

请务必记住,使用点语法设置属性也是简写。当你打电话时……</p>

self.words = mutWords;

…您实际上是在后台调用生成的访问器方法,如下所示:

[self setWords:mutWords];

由于您在copy属性上指定了属性,因此您已经告诉编译器-setWords:使用如下所示的代码生成该访问器方法:

- (void)setWords:(NSMutableArray *)words
{
    _words = [words copy];
}

了解了所有这些,您就可以看到发生了什么:生成的 setter 方法将调用-copy输入参数,并将结果分配给支持的实例变量。因为该-copy方法总是被实现为返回一个非可变对象(执行[aMutableString copy]将返回一个 NSString,依此类推)设置该属性将始终存储一个非可变副本。

于 2013-02-13T15:28:25.453 回答