Hi all,

recently I stumbled upon an interesting and slightly confusing aspect of our 
favorite language. 

I had defined a Cache class to conform to NSCoding and inherit from NSObject. 
My class’s initializer was declared init?(entries: [Entry]? = nil) at first, 
giving an option to pre-populate it when called from the NSCoding-conformant 
initializer but also simply start from scratch in my program. Failability is a 
valuable feature since the class depends on external resources that could be 
unavailable. Of course there are other means to model this, but a failable 
initializer is the most elegant one, I think.

Later on, I decided to make the Entry class private to my Cache class.

Now I had to split the initializer in a private init?(entries: [Entry]?) and an 
internal or public convenience init?().

I’ll abstract a bit now:


First version:

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

— all good. I can use it like let a = A() in my program.

Second version:

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

Now the compiler complains "failable initializer 'init()' cannot override a 
non-failable initializer" with the overridden initializer being the public 
init() in NSObject. This is the same in Swift 2 and 3.
Omitting the override does not work, the compiler now says “overriding 
declaration requires an 'override’ keyword", so it does count as overriding 
anyway. Suggested Fix-it is inserting override, giving the other error.

How comes I can effectively declare an initializer A.init?() without the 
conflict but not B.init?() ?

And: Why at all am I not allowed to override a non-failable initializer with a 
failable one? The opposite is legal: I can override a failable initializer with 
a non-failable, which even requires using a forced super.init()! and thus 
introduces the risk of a runtime error. I always have a bad feeling when I use 
!, so I try to avoid it whenever possible. I would even accept to get a 
compiler warning ;-)

To me, letting the subclass have the failable initializer feels more natural 
since an extension of functionality introduces more chance of failure. But 
maybe I am missing something here – explanation greatly appreciated.

But I found a workaround. My class now looks like this:

public class C: NSObject {
        private class X { }
        private init?(x: X?) { }
        public convenience init?(_: Void) { self.init(x: nil) }         // NO 
ERROR
}

Now I can use it like let c = C(()) or even let c = C() – which is exactly what 
I intended at first.

The fact that the new declaration does not generate an error message seems 
(kind of) legal since it (somehow) differs from the superclass initializer's 
signature. Nevertheless, using a nameless Void parameter at all and then even 
omitting it completely in the 2nd version of the call feels a bit strange… But 
the end justifies the means, I suppose. Elegance is almost preserved.

What do you think?

Sincerely,
Stefan

PS: I already posted this question on Stack Overflow 
<http://stackoverflow.com/q/38311365/1950945>, but for the more philosophical 
aspects / underpinnings, it is not the right medium — you are obliged to ask a 
concrete question there (How to?, not Why?).
_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users

Reply via email to