Reputation: 957
I have an objective-c header with the following property
@property (nullable, nonatomic, strong) NSArray<CustomObject *> *customObjects;
If I create a swift extension of that class I can now remove objects from the NSArray:
self.customObjects?.remove(at: 0)
Also if I do
print(type(of: self.customObjects))
I get:
Array<CustomObject>
Aren't NSArrays immutable ? Does Swift create a shallow copy whenever we edit it?
Upvotes: 3
Views: 294
Reputation: 126137
Your property is (implicitly) declared readwrite
in ObjC. This means you can change the property writing a new NSArray
instance that replaces the old (in which case the new instance's constants might be derived by first reading the other NSArray
instance that's the existing value of the property):
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;
In Swift, your property comes across as a Swift.Array
(that is, the Array
type from the Swift standard library), which is a value type. Every assignment semantically creates a copy. (The expensive work of performing the copy can be deferred, using a "copy on write" pattern. Arrays of reference types, like objects, copy references instead of storage, so it's essentially a "shallow copy".)
Mutating operations do this, too:
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
When you have a readwrite property in Swift, and the type of that property is a value type like Array
(or is an ObjC type that's bridged to a value type, like NSArray
), you can use mutating methods directly on the property. That's because calling a mutating method is semantically equivalent to reading (and copying) the existing value, mutating the copy, and then writing back the changed copy.
// all equivalent
self.customObjects.remove(0)
self.customObjects = self.customObjects.dropFirst(1)
var objects = self.customObjects; objects.remove(0); self.customObjects = objects
BTW: If you're designing the API for the ObjC class in question here, you might consider making your customObjects
property nonnull — unless there's a meaningful semantic difference between an empty array and a missing array, your Swift clients will find it cumbersome needing to distinguish the two.
Upvotes: 1