为什么从Swift中使用NSArray是可变的?

时间:2019-03-15 18:51:58

标签: objective-c swift nsarray

我有一个具有以下属性的Objective-C标头

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

如果我创建该类的快速扩展,现在可以从NSArray中删除对象:

self.customObjects?.remove(at: 0)

如果我愿意

print(type(of: self.customObjects))

我得到:

Array<CustomObject>

NSArrays不可变吗? Swift在我们进行编辑时会创建浅表副本吗?

1 个答案:

答案 0 :(得分:1)

您的属性在ObjC中(隐式)声明为readwrite。这意味着您可以通过写一个新的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(即Swift标准库中的Array类型)的形式出现,这是一种值类型。每个分配在语义上都会创建一个副本。 (可以使用“写时复制”模式来推迟执行复制的昂贵工作。引用类型的数组(例如对象)会复制引用而不是存储,因此本质上是“浅复制”。)

突变操作也可以这样做:

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

当您在Swift中具有readwrite属性,并且该属性的类型是诸如Array之类的值类型(或者是桥接到诸如NSArray之类的值类型的ObjC类型)时,可以直接在属性上使用变异方法。这是因为调用变异方法在语义上等同于读取(并复制)现有值,变异副本,然后写回更改后的副本。

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

顺便说一句:如果您在这里为相关的ObjC类设计API,则可以考虑将customObjects属性设置为非空-除非在空数组和缺失数组之间存在有意义的语义差异,否则您的Swift客户会发现需要区分两者很麻烦。