2016-07-11 12 views
6

Wenn ichSwift: "failable initializer 'init()' kann keine nicht-failable initializer außer Kraft setzen" vs. Standardparameter

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

erklären ist alles in Ordnung. Bei Verwendung wie let a = A() wird der Initialisierer wie erwartet aufgerufen.

Jetzt möchte ich die verschachtelte Klasse X privat haben, und die parametrisierte init ebenso (muss natürlich sein). Aber eine einfache init?() sollte wie bisher öffentlich verfügbar bleiben. Also schreibe ich

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

Aber dies gibt einen Fehler mit dem init?() initializer: failable initializer 'init()' kann keine nicht-failable initializer außer Kraft setzen mit der außer Kraft gesetzten initializer die public init() in NSObject zu sein.

Wie kann ich effektiv einen Initialisierer A.init?() ohne den Konflikt, aber nicht B.init?() deklarieren?

Bonusfrage: Warum darf ich einen nicht fehlbaren Initialisierer mit einem fehlbaren nicht überschreiben? Das Gegenteil ist legal: Ich kann einen fehlbaren Initialisierer mit einem nicht-failable überschreiben, der die Verwendung eines erzwungenen super.init()! erfordert und somit das Risiko eines Laufzeitfehlers einführt. Für mich ist es sinnvoller, die Unterklasse den fehlbaren Initialisierer verwenden zu lassen, da eine Erweiterung der Funktionalität eine größere Chance auf einen Ausfall bietet. Aber vielleicht fehlt mir hier etwas - Erklärung sehr geschätzt.

+0

'override' bedeutet eine Methode mit der gleichen Signatur in der Oberklasse. Jedoch gibt es kein 'init?' In 'NSObject'. – vadian

+0

@vadian: Ja, aber die 'override'-Anweisung lässt die Fehlermeldung' overriding declaration' erfordert ein' override'-Schlüsselwort', so dass es sowieso als überschreiben gilt. Fix-it fügt 'override' ein und gibt den anderen Fehler aus. Darüber hinaus darf ich ein Failable-Init mit einem nicht-failable überschreiben, aber nicht umgekehrt. – Stefan

Antwort

2

Nach ein bisschen fiedeln ich glaube, ich verstehe. Lassen Sie uns ein Protokoll betrachten diese initializer und eine Klasse erfordert es die Umsetzung:

protocol I { 
    init() 
} 

class A : I { 
    init() {} 
} 

Dies gibt den Fehler: "Initializer Anforderung 'init()' kann nur durch eine required initializer in nicht-final-Klasse 'A' zufrieden sein" . Dies macht Sinn, da man immer eine Unterklasse von A erklären könnte, die nicht, dass initializer nicht erben wir

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

So müssen unsere initializer in Arequired machen:

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

Nun, wenn wir schauen siehe an der NSObject Schnittstelle können wir, dass die initializer ist nichtrequired:

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

Wir können dies bestätigen Subklassen sie und fügte hinzu, eine andere initializer und versuchen, die normale zu verwenden:

class MyObject : NSObject { 
    init(n: Int) {} 
} 

MyObject() // Error: Missing argument for parameter 'n:' in call 

Jetzt kommt die seltsame Sache: Wir NSObject zum I Protokoll anzupassen erweitern können, auch wenn es nicht diese initializer erfordert:

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

ich denke ehrlich, das ist entweder ein Fehler oder eine Voraussetzung für ObjC Interop zu arbeiten (EDIT: es ist ein Fehler und bereits in der aktuellen Version behoben).Dieser Fehler sollte nicht möglich sein:

extension I { 
    static func get() -> Self { return Self() } 
} 

MyObject.get() 
// Runtime error: use of unimplemented initializer 'init()' for class '__lldb_expr_248.MyObject' 

Jetzt ist Ihre eigentliche Frage zu beantworten:

In Ihrem zweiten Codebeispiel ist der Compiler direkt, dass Sie keine nicht-failable mit einem failable initializer außer Kraft setzen kann.

In der ersten, überschreiben Sie nicht tatsächlich den Initialisierer (kein Schlüsselwort override), sondern deklarieren einen neuen, durch den der andere nicht vererbt werden kann.

Jetzt, da ich so viel geschrieben habe, bin ich nicht einmal sicher, was der erste Teil meiner Antwort mit Ihrer Frage zu tun hat, aber es ist trotzdem nett, einen Fehler zu finden.

Ich schlage vor, Sie diese stattdessen zu tun:

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

Auch am Initialization section der Swift Referenz einen Blick.

+0

Vielen Dank für Ihre ausführliche Antwort. Das zugrunde liegende Problem ist, dass der Initialisierer meiner Unterklasse ** ** fehlschlagen kann, da externe Ressourcen beteiligt sind ("NSCoding" ...), so dass es notwendig ist, zu testen, ob der Initialisierer erfolgreich war. Aber bitte sehen Sie sich meinen "PS" Nachtrag mit einem 'Void' Parameter an. – Stefan

+0

Beachten Sie, dass der oben beschriebene Fehler ('Protokoll I {init()}' gefolgt von 'Erweiterung NSObject: I {}') _does_ einen Fehler in Swift 3.0-dev liefert (_error: initializer requirement ''init()'' kann nur durch einen 'erforderlichen' Initialisierer in der nicht-finalen Klasse 'NSObject''_) befriedigt werden, so dass ich denke, dass es behoben wurde. – dfri

+0

@dfri Ohh, das ist nett, danke, dass du mich informiert hast – Kametrixom

5

Dies ist, wie ich das Problem für mich gelöst:

I

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

und es wie

let b = B(()) 

oder sogar

let b = B() 
verwenden erklären können 0

- was logisch ist, da seine Signatur (irgendwie) anders ist, also hier nicht überschrieben wird. Nur ein Void Parameter zu verwenden und es im Anruf wegzulassen fühlt sich ein bisschen komisch an ... Aber das Ende rechtfertigt die Mittel, nehme ich an. :-)

+0

Danke, ich fand das sehr nützlich für ein Realm db Modell, wo ich nur ein paar verwaltete Objektklassen hatte, die niemals mit leeren Inhalten initialisiert werden sollten, nur mit einer vollständigen Liste von Werte. Mit diesem Formular könnte ich immer noch den Realm Object Konstruktor init aufrufen (Wert: Any), da ich den Wertelabel verwenden könnte, um den Superklasseninitialisierer von meinem fehlbaren zu unterscheiden –

Verwandte Themen