Почему NSArray изменчив, когда используется из Swift? - PullRequest
3 голосов
/ 15 марта 2019

У меня есть заголовок target-c со следующим свойством

@property (nullable, nonatomic, strong) NSArray<CustomObject *> *customObjects;

Если я создаю быстрое расширение этого класса, я могу теперь удалить объекты из NSArray:

self.customObjects?.remove(at: 0)

Также, если я делаю

print(type(of: self.customObjects))

Я получаю:

Array<CustomObject>

Разве NSArrays не неизменны? Создает ли Swift поверхностную копию, когда мы ее редактируем?

1 Ответ

1 голос
/ 15 марта 2019

Ваша собственность (неявно) объявлена ​​readwrite в ObjC.Это означает, что вы можете изменить свойство, записав новый экземпляр NSArray, который заменяет старый (в этом случае константы нового экземпляра можно получить, сначала прочитав другой экземпляр NSArray, который является существующим значением свойства):

NSArray *currentObjects = self.customObjects;
// one of many ways to derive one immutable array from another:
NSArray *newArray = [currentObjects subarrayWithRange:NSMakeRange(1, currentObjects.count - 1)];
self.customObjects = newArray;

В Swift ваше свойство выглядит как Swift.Array (то есть тип Array из стандартной библиотеки Swift), который является типом значения.Каждое назначение семантически создает копию.(Дорогая работа по выполнению копирования может быть отложена с использованием шаблона «копировать при записи». Массивы ссылочных типов, таких как объекты, копируют ссылки вместо хранилища, поэтому по сути это «мелкая копия».)

Операции Mutating также делают это:

let currentObjects1 = self.customObjects
currentObjects1.remove(0) // compile error
// currentObjects1 is a `let` constant so you can't mutate it

var currentObjects = self.customObjects
currentObjects.remove(0) // ok

print(self.customObjects.count - currentObjects.count) 
// this is 1, because currentObjects is a copy of customObjects
// we mutated the former but not the latter so their count is different

self.customObjects = currentObjects
// now we've replaced the original with the mutated copy just as in the ObjC example

Когда у вас есть свойство readwrite в Swift, и тип этого свойства имеет тип значения, такой как Array (или тип ObjC, который связан сТип значения, например NSArray), вы можете использовать методы мутации непосредственно в свойстве.Это потому, что вызов метода мутации семантически эквивалентен чтению (и копированию) существующего значения, изменению копии и последующей записи измененной копии.

// all equivalent
self.customObjects.remove(0)
self.customObjects = self.customObjects.dropFirst(1)
var objects = self.customObjects; objects.remove(0); self.customObjects = objects

Кстати: если вы разрабатываетеAPI для рассматриваемого класса ObjC здесь, вы можете подумать о том, чтобы сделать ваше свойство customObjects ненулевым - если нет значимой семантической разницы между пустым массивом и отсутствующим массивом, ваши клиенты Swift сочтут это трудоемким, чтобы различать два.

...