NSCopying can be used in Swift as a generic way to create copies of classes (reference types), and as a bonus, making a Swift type inherit from NSCopying will also allow it to make use of the copy property accessor in Objective-C bridges.
The root class of most Objective-C class hierarchies, from which subclasses inherit a basic interface to the runtime system and the ability to behave as Objective-C objects.
To implement NSCopying, your object must respond to the -copyWithZone:
selector. Here’s how you declare that you conform to it:
@interface MyObject : NSObject <NSCopying> {
Then, in your object’s implementation (your .m
file):
- (id)copyWithZone:(NSZone *)zone
{
// Copying code here.
}
What should your code do? First, create a new instance of the object—you can call [[[self class] alloc] init]
to get an initialized obejct of the current class, which works well for subclassing. Then, for any instance variables that are a subclass of NSObject
that supports copying, you can call [thatObject copyWithZone:zone]
for the new object. For primitive types (int
, char
, BOOL
and friends) just set the variables to be equal. So, for your obejct Vendor, it’d look like this:
- (id)copyWithZone:(NSZone *)zone
{
id copy = [[[self class] alloc] init];
if (copy) {
// Copy NSObject subclasses
[copy setVendorID:[[self.vendorID copyWithZone:zone] autorelease]];
[copy setAvailableCars:[[self.availableCars copyWithZone:zone] autorelease]];
// Set primitives
[copy setAtAirport:self.atAirport];
}
return copy;
}
This answer is similar to the accepted, but uses allocWithZone:
and is updated for ARC. NSZone is foundation class for allocating memory. While ignoring NSZone
might work for most cases, it is still incorrect.
To correctly implement NSCopying
you must implement a protocol method which allocates a new copy of the object, with properties that match the values of the original.
In the interface declaration in the header, specify that your class implements the NSCopying
protocol:
@interface Car : NSObject<NSCopying>
{
...
}
In the .m implementation add a -(id)copyWithZone
method which looks something like the following:
- (id)copyWithZone:(NSZone*)zone
{
Car* carCopy = [[[self class] allocWithZone:zone] init];
if (carCopy)
{
carCopy.isAvailable = _isAvailable;
carCopy.transmissionType = _transmissionType;
... // assign all other properties.
}
return carCopy;
}
Just call object.copy()
to create the copy.
I didn't use copy()
for value types since those are copied "automatically." But I had to use copy()
for class
types.
I ignored the NSZone
parameter because docs say it is deprecated:
This parameter is ignored. Memory zones are no longer used by Objective-C.
Also, please note that this is a simplified implementation. If you have subclasses it gets a bit tricker and you should use dynamic type: type(of: self).init(transmissionType: transmissionType)
.
class Vendor {
let vendorId: String
var availableCars: [Car] = []
init(vendorId: String) {
self.vendorId = vendorId
}
}
extension Vendor: NSCopying {
func copy(with zone: NSZone? = nil) -> Any {
let copy = Vendor(vendorId: vendorId)
if let availableCarsCopy = availableCars.map({$0.copy()}) as? [Car] {
copy.availableCars = availableCarsCopy
}
return copy
}
}
class Car {
let transmissionType: String
var isAvailable: Bool = false
var fees: [Double] = []
init(transmissionType: String) {
self.transmissionType = transmissionType
}
}
extension Car: NSCopying {
func copy(with zone: NSZone? = nil) -> Any {
let copy = Car(transmissionType: transmissionType)
copy.isAvailable = isAvailable
copy.fees = fees
return copy
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With