Search code examples
swiftswift2gameplay-kit

How to conform to NSCopying and implement copyWithZone in Swift 2?


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
}

Solution

  • 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)
    }