Подтвердить что ты не робот

Почему свойство (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. Может ли кто-нибудь объяснить, почему это правильное поведение?

4b9b3361

Ответ 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 и т.д.), То это свойство всегда будет хранить не изменяемую копию.