Почему свойство (copy, nonatomic) NSMutableArray создает NSArrays?
Я ошибся при создании 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. Может ли кто-нибудь объяснить, почему это правильное поведение?
Ответы
Ответ 1
-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 не распознает методы изменчивого подкласса (потому что он не может мутировать его содержимое).
Ответ 2
Свойства не волшебны, они просто сокращены. Объявление @property
вашего объекта указывает компилятору создать для него переменную экземпляра базы данных и методы доступа. Фактически сгенерированный код зависит от атрибутов, которые вы задали для своего свойства.
Важно помнить, что установка свойства с использованием точечного синтаксиса также является сокращением. Когда вы звоните...
self.words = mutWords;
... вы на самом деле вызываете сгенерированный метод доступа за кулисами, например:
[self setWords:mutWords];
Поскольку вы указали атрибут copy
в своем свойстве, вы сказали компилятору сгенерировать этот метод доступа -setWords:
с кодом, который выглядит примерно так:
- (void)setWords:(NSMutableArray *)words
{
_words = [words copy];
}
Зная все это, вы можете видеть, что происходит: генерируемый метод setter вызывает -copy
на входном аргументе и присваивает результат переменной экземпляра резервной копии. Поскольку метод -copy
всегда реализуется для возврата неперемещаемого объекта (выполнение [aMutableString copy]
вернет NSString и т.д.), То это свойство всегда будет хранить не изменяемую копию.