I am trying to understand why Swift enforces a class that conforms to a protocol with an initializer to be marked as required. This essentially enforces any subclasses to also implement that initializer. Surely the designated superclass initializer would be inherited?
The quotes below are taken from the Swift Language Guide:
https://developer.apple.com/library/prerelease/content/documentation/Swift/Conceptual/Swift_Programming_Language/Protocols.html#//apple_ref/doc/uid/TP40014097-CH25-ID272
You can implement a protocol initializer requirement on a conforming
class as either a designated initializer or a convenience initializer.
In both cases, you must mark the initializer implementation with the
required modifier:
class SomeClass: SomeProtocol {
required init(someParameter: Int) {
// initializer implementation goes here
}
}
class SomeSubclass: SomeClass {
required init(someParameter: Int) { // enforced to implement init again
// initializer implementation goes here
}
}
The use of the required modifier ensures that you provide an explicit
or inherited implementation of the initializer requirement on all
subclasses of the conforming class, such that they also conform to the
protocol.
EDIT:
I had not initially mentioned that I am currently limited to Swift 2.1. It appears to be a compiler issue in this release and does not occur in later versions.
Surely the designated superclass initializer would be inherited?
No, not always. If the subclass defines its own designated initialisers, then it won't automatically inherit the superclass' designated initialisers. Consider the following example:
class Foo {
init() {}
}
class Bar : Foo {
var str: String
init(str: String) {
self.str = str
}
}
let b = Bar() // illegal – what value would the 'str' property have?
As Bar defines its own init(str:) designated initialiser, it doesn't automatically inherit Foo's designated initialiser init(). This prevents unsafe initialisation in cases where the subclass declares its own stored properties.
Marking init() as required enforces Bar has an init(), be it through providing its own implementation:
class Foo {
required init() {}
}
class Bar : Foo {
var str: String
init(str: String) {
self.str = str
}
// implement required init(), as Bar defines its own designated initialiser.
required init() {
self.str = "foo" // now str is correctly initialised when calling init()
}
}
let b = Bar() // now legal
Or by inheriting Foo's implementation (when Bar doesn't define its own designated initialisers):
class Foo {
required init() {}
}
class Bar : Foo {
// inherits init() from Foo, as Bar doesn't define its own designed initialisers.
}
let b = Bar() // legal
You're not force to implement the initializer in the subclass. Consider this example, which compiles just fine:
protocol SomeProtocol {
init(someParameter: Int)
}
class SomeClass: SomeProtocol {
required init(someParameter: Int) {
// initializer implementation goes here
print(someParameter)
}
}
class SomeSubclass: SomeClass {
// Notice that no inits are implemented here
}
_ = SomeClass(someParameter: 123)
_ = SomeSubclass(someParameter: 456)
Related
Can you tell me why the superclass initializer gets called because I never call it from the initializer in subclass ?
Regarding the two step process of the initialization, I thought that the compiler would throw an error because I didn't call super.init() actually
class Superclass {
var a: Int
init() {
self.a = 1
}
}
class Subclass: Superclass {
var b: Int
override init() {
self.b = 2
}
}
var subclass = Subclass()
print(subclass.b)
// Print 2
print(subclass.a)
// Print 1 => How is it possible as I never call super.init() ?
The compiler can synthesise a call to super.init() in a designated initialiser if you don't make the call yourself. So in your case the compiler effectively transforms your code to:
class Superclass {
var a: Int
init() {
self.a = 1
}
}
class Subclass : Superclass {
var b: Int
override init() {
self.b = 2
super.init() // synthesised by the compiler
}
}
This also applies to cases where you're not overriding the superclass' init():
class Subclass : Superclass {
var b: Int
init(b: Int) {
self.b = b
super.init() // also synthesised by the compiler (try removing)
}
}
Note that this synthesis comes with some restrictions, it only applies when:
The superclass has one and only one designated initialiser
This designated initialiser doesn't have any parameters, i.e init()
In all other cases you need to call super.init yourself.
I have a method in Swift with a signature of the form:
func myMethod<T>(class: T.Type) where T: SomeClass & MyProtocol
I would like to have a variable that is an array of classes that are all subclasses of SomeClass and conform to MyProtocol.
It would look something like:
let classArray = [SubclassOfSomeClass.self, SubclassTwoOfSomeClass.self, SubclassThreeOfSomeClass.self]
Where each of SubclassOfSomeClass, SubclassTwoOfSomeClass and SubclassThreeOfSomeClass conform to MyProtocol
I would then like to call myMethod as follows:
classArray.forEach { classType in
myMethod(class: classType)
}
I receive the compiler error "Generic parameter T could not be inferred". This makes sense to me as the type of classArray variable is inferred as [MyClass.Type].
My question is, can and how do I define the variable classArray such that the compiler knows that every type defined therein conforms to the protocol MyProtocol.
You can achieve that by having SomeClass conform to MyProtocol. Therefore every subclass would also have to conform to MyProtocol.
Then your method signature would be just:
func myMethod<T>(class: T.Type) where T: SomeClass
If that is not desired then you can change the type of myMethod to not be generic. I think swift is unable to type the array to have elements of (SomeClass & MyProtocol).Type. In your case you are not really using a generic object. You do have a specific type in mind and it is (SomeClass & MyProtocol).Type. Your code would look something like this:
protocol MyProtocol: class {
}
class SomeClass: NSObject {
}
class SubClass: SomeClass, MyProtocol {
}
class SubClass2: SomeClass, MyProtocol {
}
let classArray: [(SomeClass & MyProtocol).Type] = [SubClass.self, SubClass2.self]
func myMethod(class: (SomeClass & MyProtocol).Type) {
}
classArray.forEach {
myMethod(class: $0)
}
protocol MyProtocol {
var x: Int { get }
}
class MyClass: NSObject {
var y: Int = 10
}
class SubClass1: MyClass, MyProtocol {
private(set) var x: Int = 9
}
class SubClass2: MyClass {
}
class SubClass3: MyClass, MyProtocol {
var x: Int { return 15 }
}
let items: [MyClass & MyProtocol] = [SubClass1(), SubClass3()]
func myMethod(_ item: MyClass & MyProtocol) {}
classArray.forEach { myMethod($0) }
So for now you can use just MyClass & MyProtocol
I'd like to make a subclass of a generic class that is itself a subclass but I cannot override the initializer.
To clarify:
class BaseClass {
init(printMe: String) {
print(printMe)
}
}
class GenericClass<T>: BaseClass {
var object: T?
}
class NotGenericClass: GenericClass<NSNumber> {
override init(printMe: String) { // <- Error here
super.init(printMe: printMe)
// More setup
}
}
let a = NotGenericClass(printMe: "Print this")
a.object = NSNumber(int: 1)
The override init(printMe: String) line in NotGenericClass gives the following error:
Initializer does not override a designated initializer from its superclass
If GenericClass did not take a type parameter, this code would work. Is this a bug with Swift or am I misunderstanding how subclassing generic classes should work?
It seems that GenericClass doesn't automatically inherit the initializer in this case.
This makes it work:
class GenericClass<T>: BaseClass {
var object: T?
// Explicitly provide an initializer
override init(printMe: String) {
super.init(printMe: printMe)
}
}
This isn't clear from the Automatic Initializer Inheritance section of the docs, which claims that "If your subclass doesn’t define any designated initializers, it automatically inherits all of its superclass designated initializers". You might want to file a bug.
I've searched quite a bit around and played within a playground but I had no success, so I ask here:
Any way to have variable containing a non-#objc protocol metatype and then call class/static methods from it?
e.g.:
protocol MyProtocol {
class func myFunc() -> Int
}
enum MyEnum: Int, MyProtocol {
case A
case B
static func myFunc() -> Int { return A.rawValue }
}
let foo: MyProtocol.Type = MyEnum.self
let bar = foo.myFunc()
p.s. it says the last call is unimplemented, so should I expect it in a future Swift release?
UPDATED for Swift Version 2.0 and above
Swift 2.0+ allows methods to be declared as static in the protocol definition. These must be satisfied with static/class methods in objects that implement the protocol.
You cannot satisfy a protocol definition for a instance method with a static method or vice-versa, which makes this an incomplete answer for the question above.
If you want to try this just use the keyword "static" in your protocol definition for methods you will implement as static or class methods in your conforming objects:
protocol InstanceVsStatic {
func someInstanceFunc()
static func someStaticFunc()
}
enum MyConformingEnum: InstanceVsStatic {
case someCase
static func someStaticFunc() {
// code
}
func someInstanceFunc() {
// code
}
}
class MyConformingClass: InstanceVsStatic {
class func someStaticFunc() {
// code
}
func someInstanceFunc() {
// code
}
}
struct MyConformingStruct: InstanceVsStatic {
static func someStaticFunc() {
// code
}
func someInstanceFunc() {
// code
}
}
You can have an instance method call a static/class method:
This allows you to execute static code when you need to conform to a protocol that requires an instance method.
struct MyConformingStruct: InstanceVsStatic {
static func doStuffStatically(){
// code
}
static func someStaticFunc() {
// code
}
func someInstanceFunc() {
MyConformingStruct.doStuffStatically()
}
}
Swift 1.2
Other than indirectly as above, there is no way to use static (class) methods to conform to a protocol in pure swift version 1.2 and below. It is a known bug / unimplemented feature: https://openradar.appspot.com/20119848
Class methods are only allowable in classes and protocols; an enum is neither.
Class methods are one type of Swift "type methods"; the other type is a static method, which is what you've declared in your enum. Class and static methods are different.
See the Swift methods docs for a few different patterns and examples.
p.s. it says the last call is unimplemented, so should I expect it in a future Swift release?
Yes.
I defined a simple class:
class MyClass {
var name:String?
required init() {
println("init")
}
}
I can add a new initializer in an extension like this:
extension MyClass {
convenience init(name: String) {
self.init()
self.name = name
}
}
Everything works fine.
But as soon as I define the new initializer in a protocol:
protocol MyProtocol {
init(name:String)
}
And make my extension conform to that protocol:
extension MyClass : MyProtocol {
convenience init(name: String) {
self.init()
self.name = name
}
}
I get the following error:
Initializer requirement 'init(name:)' can only be satisfied by a
required initializer in the definition of non-final class 'MyClass'
What is going on here?
(BTW: I can't make my class final, because this is only the extract of a more complicated use case.)
Ok, my bad.
To guarantee that all subclasses conform to MyProtocol new initializer has to be marked as required as well.
Furthermore Swift requires to declare all required initializers directly within the class and does not allow to declare them in extensions.
extension MyClass : MyProtocol {
required convenience init(name: String) {
self.init()
self.name = name
}
}