How to conform to NSCopying and implement copyWithZone in Swift 2?
Asked Answered
F

3

23

I would like to implement a simple GKGameModel in Swift 2. Apple's example is expressed in Objective-C and includes this method declaration (as required by protocol NSCopyingfrom which GKGameModel inherits):

- (id)copyWithZone:(NSZone *)zone {
    AAPLBoard *copy = [[[self class] allocWithZone:zone] init];
    [copy setGameModel:self];
    return copy;
}

How does this translate into Swift 2? Is the following appropriate in terms of efficiency and ignoring zone?

func copyWithZone(zone: NSZone) -> AnyObject {
    let copy = GameModel()
    // ... copy properties
    return copy
}
Facile answered 20/8, 2015 at 7:19 Comment(0)
M
43

NSZone is no longer used in Objective-C for a long time. And passed zone argument is ignored. Quote from the allocWithZone... docs:

This method exists for historical reasons; memory zones are no longer used by Objective-C.

You're safe to ignore it as well.

Here's an example how to conform to NSCopying protocol.

class GameModel: NSObject, NSCopying {

  var someProperty: Int = 0

  required override init() {
    // This initializer must be required, because another
    // initializer `init(_ model: GameModel)` is required
    // too and we would like to instantiate `GameModel`
    // with simple `GameModel()` as well.
  }

  required init(_ model: GameModel) {
    // This initializer must be required unless `GameModel`
    // class is `final`
    someProperty = model.someProperty
  }

  func copyWithZone(zone: NSZone) -> AnyObject {
    // This is the reason why `init(_ model: GameModel)`
    // must be required, because `GameModel` is not `final`.
    return self.dynamicType.init(self)
  }

}

let model = GameModel()
model.someProperty = 10

let modelCopy = GameModel(model)
modelCopy.someProperty = 20

let anotherModelCopy = modelCopy.copy() as! GameModel
anotherModelCopy.someProperty = 30

print(model.someProperty)             // 10
print(modelCopy.someProperty)         // 20
print(anotherModelCopy.someProperty)  // 30

P.S. This example is for Xcode Version 7.0 beta 5 (7A176x). Especially the dynamicType.init(self).

Edit for Swift 3

Below is the copyWithZone method implementation for Swift 3 as dynamicType has been deprecated:

func copy(with zone: NSZone? = nil) -> Any
{
    return type(of:self).init(self)
}
Misericord answered 20/8, 2015 at 8:44 Comment(2)
I tried implementing this for my class, but how can I create a copy while copying the class's variables as well? For now, I am getting a fresh instance which does not have the properties copied from the old instance.Verulamium
How to copy all properties of a class at once rather than doing the assignment one by one. Because you see i have a class with around 50 properties, and there is a high chance that i might miss assignment of some of them. Any way out ?Truckload
C
2

Swift4, Helium's PlayItem object:

// MARK:- NSCopying
convenience required init(_ with: PlayItem) {
    self.init()

    self.name  = with.name
    self.link  = with.link
    self.date  = with.date
    self.time  = with.time
    self.rank  = with.rank
    self.rect  = with.rect
    self.plays = with.plays
    self.label = with.label
    self.hover = with.hover
    self.alpha = with.alpha
    self.trans = with.trans
    self.agent = with.agent
    self.tabby = with.tabby
}

func copy(with zone: NSZone? = nil) -> Any
{
    return type(of:self).init(self)
}
Component answered 24/8, 2019 at 12:42 Comment(0)
T
0

A COPY OF ALREADY PRESENT array last element of dictionary is copied and created with values.

Worked in Xcode 12.1 swift 5

// Model class where the model is defined for the class

import Foundation
   class SampleClass: NSObject, NSCopying, Codable {
    
    var variable1: String?
    var variable2: String?
    var variable3: String?
    
    required override init() {

      }
    
    required init(_ model: SampleClass) {

        self.variable1 = model.variable1
        self.variable2 = model.variable2
        self.variable3 = model.variable3
    }
    
    func copy(with zone: NSZone? = nil) -> Any
    {
        return type(of:self).init(self)
    }
    
    init(_ dictionary: [String: Any]) {
        
    self.variable1 = dictionary["variable1"] as? String
    self.variable2 = dictionary["variable2"] as? String
    self.variable3 = dictionary["variable3"] as? String
    }

}

// How to use it in ViewController in a button where data from the model is already in a array of dictionary

 @IBAction func duplicate(_ sender: Any) {
        
        if self.currentArray > 0 {
            if let content = self.self.currentArray.last {
                let anotherCopy = content.copy() as! SampleClass
                self.self.currentArray.append(anotherCopy)
            }
        }
    }
Terefah answered 14/5, 2021 at 13:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.