How to implement copy constructor in Swift subclass?
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
}
}
Implementing NSCopying in Swift with subclasses
The Short Answer
You cannot use self.dynamicType()
without marking init()
as required
because there's no guarantee subclasses of Vehicle
will also implement init()
.
Exploring The Problem
Taking a look at The Swift Programming Language: Initialization, it's mentioned how
subclasses do not inherit their superclass initializers by default
The situations in which a subclass will inherit its superclass' initialisers are:
Assuming that you provide default values for any new properties you
introduce in a subclass, the following two rules apply:Rule 1
If your subclass doesn’t define any designated initializers, it
automatically inherits all of its superclass designated initializers.Rule 2
If your subclass provides an implementation of all of its superclass
designated initializers—either by inheriting them as per rule 1, or by
providing a custom implementation as part of its definition—then it
automatically inherits all of the superclass convenience initialisers.
Take a look at the example:
class MySuperclass {
let num = 0
// MySuperclass is given `init()` as its default initialiser
// because I gave `num` a default value.
}
class MySubclass : MySuperclass {
let otherNum: Int
init(otherNum: Int) {
self.otherNum = otherNum
}
}
According to the information above, since MySubclass
defined the property otherNum
without an initial value, it doesn't automatically inherit init()
from MySuperclass
.
Now suppose I want to add the following method to MySuperclass
:
func myMethod() {
println(self.dynamicType().num)
}
You'll get the error you described because there is no guarantee subclasses of MySuperclass
will implement init()
(and in this example they don't).
To solve this problem you therefore need to mark init()
as required
, to ensure all subclasses of MySuperclass
implement init()
, and so calling self.dynamicType()
is a valid thing to do. It's the same problem as in your question: Swift knows Vehicle
implements init()
, however it doesn't know any subclasses will implement init()
and so you need to make it required
.
An alternative solution, which isn't suitable in your example, is to mark Vehicle
as final
, meaning Vehicle
can't be subclassed. Then you'll be able to use self.dynamicType()
; but you might as well just use Vehicle()
in that case.
Right way to implement deep copy for object which contains nested objects in Swift?
Assuming your filter choice also conforms:
let copy = ZLFilters(filterChoices: filterChoices!.copyWithZone(zone))
Instantiate Self outside of init in Swift
You may use the (dynamically typed) metatype returned by type(of:)
to access an initializer of the concrete type of the metatype. Quoting the Language Reference - Metatypes
Use an initializer expression to construct an instance of a type from
that type’s metatype value. For class instances, the initializer
that’s called must be marked with therequired
keyword or the entire
class marked with the final keyword.
So in your case, you could use the metatype of self
to call a required
initializer of the concrete type of self
, e.g.
func myCustomCopy() -> Self {
return type(of: self).init()
}
Note that, as specified in the quote above, since you are working with a non-final class, the initializer must be a required
one.
Call parent constructor when init a subclass in Swift
The rules from Apple state that:
[S]ubclasses do not inherit their superclass initializers by default. However, superclass initializers are automatically inherited if certain conditions are met. In practice, this means that you do not need to write initializer overrides in many common scenarios, and can inherit your superclass initializers with minimal effort whenever it is safe to do so.
Assuming that you provide default values for any new properties you introduce in a subclass, the following two rules apply:
Rule 1:
If your subclass doesn’t define any designated initializers, it automatically inherits all of its superclass designated initializers.Rule 2:
If your subclass provides an implementation of all of its superclass designated initializers—either by inheriting them as per rule 1, or by providing a custom implementation as part of its definition—then it automatically inherits all of the superclass convenience initializers.These rules apply even if your subclass adds further convenience initializers.
What does all that mean? If you make your subclass B
's initializer a convenience initializer, then then B
should inherit all of A
's designated initializers.
class B: A {
convenience init(aString: String) { ... }
}
Related Topics
How to Tell If a Node Is on the Screen Spritekit Swift
How to Convert a String to a Cstring in the Swift Language
Swift 3: Convert a Null-Terminated Unsafepointer<Uint8> to a String
Non-Strong References Not Working in Playground
How to Create Several Cached Uicolor
How to Rotate Only One View Controller to Landscape Orientation in iOS Swift 3
How to Declare an Inline Function in Swift
Present Uiviewcontroller as a Modal with Transparent Background
Swift 3: Atomic_Compare_Exchange_Strong
Converting Swift Array to Cfarray in Xcode 8 (Swift 3)
Background Request Not Execute Alamofire Swift
How to Execute Code Once and Only Once in Swift
Constant Speed Orbit Around Point with Sknode
Know When an Iteration Over Array with Async Method Is Finished
Call Completion Block When Two Other Completion Blocks Have Been Called
How to Install Xcode on an External Hard Drive Along with the iPhone Simulator.App
Swift's JSONdecoder with Multiple Date Formats in a JSON String