How to implement copy constructor in Swift subclass?

The simplest way to do it would simply be to change the name of the subclass initialiser to init(copyFromSquare: Square), leaving Square with the init(copyFrom: Shape) method intact (as you have contracted by inheriting from Shape).

You could of course override init(copyFrom: Shape), and test whether copyFrom is a Square, in which case you take one course of action (set the length), otherwise not.

Note also that you need to set self.length before you call the super.

class Shape : NSObject {
    var color : String

    override init() {
        color = "Red"
    }

    init(copyFrom: Shape) {
        color = copyFrom.color
    }
}

class Square : Shape {
    var length : Double

    override init() {
        self.length = 10.0
        super.init()
    }

    override init(copyFrom: Shape) {
        if copyFrom is Square {
            self.length = (copyFrom as Square).length
        } else {
            self.length = 10.0 // default
        }
        super.init(copyFrom: copyFrom)
    }
}

init(copyFrom: Square) is an overload, not an override, of init(copyFrom: Shape). What I mean is that they are unrelated methods because they accept different types. In Swift that's acceptable. In ObjC, that's illegal. There are no overloads in ObjC.

Swift initializers don't automatically inherit. So in Swift, you couldn't try to copy a random Shape as a Square. The initializer isn't available. But in ObjC, initializers do automatically inherit (and you can't stop them from doing so). So if you have a method initWithCopyFrom:(*Shape), it is required that every subclass be willing to accept it. That means you could (in ObjC) try to create a copy of a Circle as a Square. That's of course nonsense.

If this is an NSObject subclass, you should use NSCopying. Here's how you would go about that:

import Foundation

class Shape : NSObject, NSCopying { // <== Note NSCopying
  var color : String

  required override init() { // <== Need "required" because we need to call dynamicType() below
    color = "Red"
  }

  func copyWithZone(zone: NSZone) -> AnyObject { // <== NSCopying
    // *** Construct "one of my current class". This is why init() is a required initializer
    let theCopy = self.dynamicType()
    theCopy.color = self.color
    return theCopy
  }
}

class Square : Shape {
  var length : Double

  required init() {
    length = 10.0
    super.init()
  }

  override func copyWithZone(zone: NSZone) -> AnyObject { // <== NSCopying
    let theCopy = super.copyWithZone(zone) as Square // <== Need casting since it returns AnyObject
    theCopy.length = self.length
    return theCopy
  }

}

let s = Square()      // {{color "Red"} length 10.0}

let copy = s.copy() as Square // {{color "Red"} length 10.0} // <== copy() requires a cast

s.color = "Blue"               // {{color "Blue"} length 10.0}
s                              // {{color "Blue"} length 10.0}
copy                           // {{color "Red"}

Swift 3

class Shape: NSObject, NSCopying {

    required override init() {
        super.init()
    }    

    func copy(with zone: NSZone? = nil) -> Any {
        let copy = type(of: self).init()
        return copy
    }

}

class Square: Shape {

    required override init() {
        super.init()
    }    

    func copy(with zone: NSZone? = nil) -> Any {
        let copy = super.copy(with: zone) as! Square
        copy.foo = self.foo
        ......
        return copy
    }

}

Tags:

Swift