Protocol Extension Initializer

How to define initializers in a protocol extension?

As you can see this doesn't work under these circumstances because when compiling, one has to make sure that all properties are initialized before using the struct/enum/class.

You can make another initializer a requirement so the compiler knows that all properties are initialized:

protocol Car {
var wheels : Int { get set }
// make another initializer
// (which you probably don't want to provide a default implementation)
// a protocol requirement. Care about recursive initializer calls :)
init()
init(wheels: Int)

}

extension Car {
// now you can provide a default implementation
init(wheels: Int) {
self.init()
self.wheels = wheels
}
}

// example usage

// mark as final
final class HoverCar: Car {
var wheels = 0
init() {}
}

let drivableHoverCar = HoverCar(wheels: 4)
drivableHoverCar.wheels // 4

As of Xcode 7.3 beta 1 it works with structs as expected but not with classes since if they are not final the init(wheels: Int) in the protocol is a required init and it can be overridden therefore it cannot be added through an extension. Workaround (as the complier suggests): Make the class final.

Another workaround (in depth; without final class)

To work with classes without making them final you can also drop the init(wheels: Int) requirement in the protocol. It seems that it behaves no different than before but consider this code:

protocol Car {
var wheels : Int { get set }
init()
// there is no init(wheels: Int)
}

extension Car {
init(wheels: Int) {
self.init()
print("Extension")
self.wheels = wheels
}
}

class HoverCar: Car {
var wheels = 0
required init() {}
init(wheels: Int) {
print("HoverCar")
self.wheels = wheels
}
}

// prints "HoverCar"
let drivableHoverCar = HoverCar(wheels: 4)

func makeNewCarFromCar<T: Car>(car: T) -> T {
return T(wheels: car.wheels)
}

// prints "Extension"
makeNewCarFromCar(drivableHoverCar)

So if you make a Car from a generic context where the type on which you call init is only to be known as Car the extension initializer is called even though an initializer is defined in HoverCar. This only occurs because there is no init(wheels: Int) requirement in the protocol.

If you add it you have the former problem with declaring the class as final but now it prints two times "HoverCar". Either way the second problem probably never occurs so it might be a better solution.

Sidenote: If I have made some mistakes (code, language, grammar,...) you're welcome to correct me :)

Calling a protocol extension initializer

An init within a protocol is required, and therefore has to be implemented explicitly i.e. a default implementation cannot be utilised.

As for 'explicitly calling a protocol extension's initializer', you cannot instantiate a protocol type.

I would suggest using inheritance for this.

Protocol Extension Initializer forcing to call self.init

Consider this example:

protocol P {
init()
}

extension P {
init() {

} // error: 'self.init' isn't called on all paths before returning from initializer
}

struct S : P {
var str: String
}

let s = S()
print(s.str)

Suppose it compiled – we'd be able to create an S value without providing a value for the str property. That's why the compiler is complaining that your protocol extension implementation of init() isn't calling self.init. It needs you to chain to some other initialiser requirement – one that you don't provide a default implementation for (otherwise you could get into a recursive loop, as you found out), and therefore one that the adopting type needs to implement such that it can fully initialise itself.

For example, this is legal:

protocol P {
init()
init(str: String)
}

extension P {
init() {
self.init(str: "some default")
}
}

struct S : P {
var str: String
}

let s = S()
print(s.str) // some default

because now we're chaining to the init(str:) requirement, which S must implement (in this case it's satisfied by the implicit memberwise initialiser).

Protocol Extension Initializer

You have to provide a valid chain of init for creating an instance of a class and that limits your options for initializers in protocols.

Since your protocol can't be certain to cover all members of the class that uses it, any initializer you declare in your protocol will need to delegate initialization of the "unknown" members of the class to another initializer provided by the class itself.

I adjusted your example to illustrate this using a basic init() as the delegation initializer for the protocol.

As you can see, this requires that your class implement initial values for all members when init() is called. In this case I did that by providing default values in each member's declaration. And, since there isn't always an actual initial value for some members, I changed them to auto-unwrap optionals.

And to make thinks more interesting, your class cannot delegate initialization to a protocol supplied initializer unless it does so through a convenience initializer.

I wonder if all these restrictions are worth the trouble. I suspect you're trying to use a protocol because you need a bunch of common variables to be initialized consistently between classes that implement the protocol. Perhaps using a delegate class would provide a less convoluted solution than protocols (just a thought).

protocol Thing:AnyObject
{
var color:UIColor! { get set }
init()
}

extension Thing
{
init(color:UIColor)
{
self.init()
self.color = color
}
}

class NamedThing:Thing
{
var name:String! = nil
var color:UIColor! = nil

required init() {}

convenience init(name:String,color:UIColor)
{
self.init(color:color)
self.name = name
}
}

Why can't you initialize a protocol that has default implementation?

A protocol is not a concrete type, so when you would call Protocol.init(), the compiler wouldn't know what concrete type to initialise. It would only know that the type you want to initialise has the interface described by Protocol.

The compiler has to know the concrete type because of the memory allocation. A protocol only defines a subset of required properties (and methods) that its conformant types must have, but doesn't define the whole set of properties its conformant types will have, since that actually differs type-by-type. Due to this, if you were to able to initialise a protocol type, the compiler and the runtime would have no idea about how much memory to allocate for that particular object, which is a piece of information without which no object can be initialised.

Default initializer in protocol

This, if it did anything, would be an infinite loop:

extension ProtWithInit {
init() {
self.init()
print("Hello protocol")
}
}

Structs and protocols are not classes. They do not have class-like inheritance. (Even if this were a class, this would be an infinite loop, since self.init() would just call init() again.)

Go back to your calling code, the code you say "so that I can run code on all structs conforming to a specific protocol." What do that code look like. What does it actually require on real types? Use that to work out your protocol. For example, the following would be similar to what you're describing:

extension ProtWithInit {
static func create() -> Self {
print("Hello protocol")
return Self.init()
}
}

let myStruct = MyStruct.create()

Swift protocol extension self reference issues with init

Your question is almost the same as in this post I answered yesterday.

Here is the trick to solve this :)

protocol TestProtocol {
var myVar : Double { get set }
init() // designated initializer which will ensure that your class or structer type will instantiate correctly
}

struct TestStruct : TestProtocol {
var myVar : Double

init() {
myVar = 0
}
}

extension TestProtocol {
init(value : Double) {
self.init()
myVar = value
}

init(existingStruct : TestProtocol) {
self.init()
myVar = existingStruct.myVar
}
}

Have a good day. :) Protocol extension is so nice.

Public default init in protocol

MyClass is supposed to conform to given protocol MyProtocol, even if MyClass is public. But how would MyClass conform to MyProtocol, if MyClass were public but its init not visible from the other module? It would conform to MyProtocol(signature says so) and at the same time not(no visible init).

Since your protocol can't be certain to cover all members of the class that uses it, any initializer you declare in your protocol will need to delegate initialization of the "unknown" members of the class to another initializer provided by the class itself.

Source

Since MyClass‘ default initializer is internal the protocol does not conform to the protocol if no public init is declared in-class while MyClass is public.



Related Topics



Leave a reply



Submit