我有一个具有以下属性的Objective-C标头
@property (nullable, nonatomic, strong) NSArray<CustomObject *> *customObjects;
如果我创建该类的快速扩展,现在可以从NSArray中删除对象:
self.customObjects?.remove(at: 0)
如果我愿意
print(type(of: self.customObjects))
我得到:
Array<CustomObject>
NSArrays不可变吗? Swift在我们进行编辑时会创建浅表副本吗?
答案 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客户会发现需要区分两者很麻烦。