Swift: "failable initializer" init () "cannot override the initializer without fail" compared to the default parameters

If I announce

public class A: NSObject { public class X { } public init?(x: X? = nil) { } } 

everything is fine. When used as let a = A() initializer is invoked as expected.

Now I would like to have an X private nested class, as well as a parameterized init (should be, of course). But simple init?() Should remain publicly available, as before. Therefore i write

 public class B: NSObject { private class X { } private init?(x: X?) { } public convenience override init?() { self.init(x: nil) } } 

But this leads to an error with the init?() Initializer: the failable initializer 'init ()' cannot override the initializer without failing with the overridden initializer being public init() in NSObject .

How can I effectively declare an A.init?() initializer A.init?() Without conflict, but not B.init?() ?

Bonus question: why am I not allowed to override the initializer without failures with failover? The opposite is legal: I can override the initializer with a fault-free error, which requires the use of forced super.init()! and thus poses a risk of runtime errors. For me, letting the subclass generate an error-generated initializer feels more reasonable, because expanding the functionality creates a better chance of failure. But maybe something is missing for me - the explanation is much appreciated.

+6
source share
2 answers

Here is how I solved the problem for me:

I can announce

 public convenience init?(_: Void) { self.init(x: nil) } 

and use it like

 let b = B(()) 

or even

 let b = B() 

- this is logical, since its signature (type) is different, so there is no redefinition here. Just using the Void parameter and dropping it in the call is a bit strange ... But the end justifies the means, I suppose. :-)

+5
source

After messing around a bit, I think I understand. Consider a protocol that requires this initializer and a class that implements it:

 protocol I { init() } class A : I { init() {} } 

This gives an error: “The initializer requirement“ init () ”can be satisfied only by the required initializer in the non-final class“ A. ”This makes sense, since you can always declare a subclass of A that does not inherit this initializer:

 class B : A { // init() is not inherited init(n: Int) {} } 

So, we need to make our initializer in A required :

 class A : I { required init() {} } 

Now, if we look at the NSObject interface, we will see that the initializer is not required :

 public class NSObject : NSObjectProtocol { [...] public init() [...] } 

We can confirm this by subclassing it by adding another initializer and trying to use the usual one:

 class MyObject : NSObject { init(n: Int) {} } MyObject() // Error: Missing argument for parameter 'n:' in call 

Now the strange NSObject came: we can extend NSObject to conform to protocol I , even if it does not require this initializer:

 extension NSObject : I {} // No error (!) 

I honestly believe that this is either a bug or a requirement for ObjC to work together (EDIT: bug and has already been fixed in the latest version). This error may not be possible:

 extension I { static func get() -> Self { return Self() } } MyObject.get() // Runtime error: use of unimplemented initializer 'init()' for class '__lldb_expr_248.MyObject' 

Now, to answer your real question:

In your second code example, the compiler is right that you cannot override non-failable with a failover initializer.

In the first case, you do not actually override the initializer keyword (no override ), but instead declare a new one that cannot be inherited by another.

Now that I have written this a lot, I'm not even sure that the first part of my answer is related to your question, but in any case it is nice to find an error.

I suggest you do this instead:

 public convenience override init() { self.init(x: nil)! } 

Also see the Initialization Swift help topic .

+2
source

All Articles