2016-04-12 2 views
5

Okay, dann ist dies zwar ein bisschen eine Best-Practice-Frage, aber ich will es richtig machen, so hoffentlich jemand kann mich aufklären:Wie NS_DESIGNATED_INITIALIZER verwenden und Init überschreiben?

Szenario ist ziemlich Standard, hat aber eine Wendung: Ich habe eine Klasse in Ein Framework schreibe ich, das direkt von NSObject erbt. Es hat einen designierten Initialisierer mit einigen wenigen Argumenten, von denen die meisten nonnull sind. Da die Sache Teil eines Frameworks ist, verwende ich explizit das Makro NS_DESIGNATED_INITIALIZER (was ich in kleineren, persönlichen Apps nicht immer mache). Das Problem ist, dass dies dazu führt, dass XCode mich warnt, auch init zu überschreiben, d. H. Den benannten Initialisierer der Oberklasse. Aber dazu verlangt es auch, dass ich meinen designierten Initializer ausrufe, was ich nicht tun kann, weil mir einfach sinnvolle Vorgaben für seine Argumente fehlen. Ich möchte nicht wirklich eine Ausnahme in der "kleinen" init werfen, würde ich lieber lieber nil zurückgeben.

der Warnung Um loszuwerden, habe ich init als zweite bezeichnet initalizer in Verlängerung meiner Klasse ist wie so:

@interface MyClassName() 
// some other stuff not relevant` 

-(nullable instancetype)init NS_DESIGNATED_INITIALIZER; 

@end 

Jetzt kann ich sicher nur return nil; in dem außer Kraft gesetzt init Methode, wie ich wollte. Das bedeutet meine Dokumentation (ich benutze appledoc) und durch Erweiterung XCode Code-Vervollständigung wird nicht sagen, jemand mit meinem Framework, ist eigentlich auch ein designierter Initialisierer (damit sie nicht versehentlich verwenden), aber es ist immer noch da (in Unit-Tests zum Beispiel könnte dies nützlich sein).

Meine Frage ist: Gibt es irgendwelche Gefahren zu diesem abgesehen davon, dass jemand es tatsächlich in der Produktion verwendet, glücklich, Nachrichten ohne Erfolg nach Null zu senden? Wäre dies einer der wenigen Fälle, in denen das Auslösen einer Ausnahme in init vorzuziehen wäre?

Antwort

6

Anstatt einfach nil von (und vielleicht fügen Sie einen Kommentar, dass Sie es nicht nennen) zurückgeben - sollten Sie es als nicht verfügbar markieren.

Damit wird nicht nur die Warnung gelöscht, dass Sie NSObject 's Initialisierer nicht überschreiben - es wird auch einen Fehler bei der Kompilierung erzeugen, wenn jemand versucht, init anstelle des angegebenen Initialisierers aufzurufen.

Um dies zu tun, können Sie entweder das NS_UNAVAILABLE Makro verwenden oder das nicht verfügbare __attribute__ wie von this answer gezeigt verwenden. Der Vorteil der Verwendung des __attribute__ ist, dass Sie eine Nachricht angeben können, die der Compiler dem Benutzer präsentiert.

Zum Beispiel:

@interface Foo : NSObject 

-(instancetype) init __attribute__((unavailable("You cannot create a foo instance through init - please use initWithBar:"))); 

-(instancetype) initWithBar:(Bar*)bar NS_DESIGNATED_INITIALIZER; 

@end 

... 


Foo* fooA = [[Foo alloc] init]; // ERROR: 'init' is unavailable: You cannot create a foo instance through init - please use initWithBar: 

Foo* fooB = [[Foo alloc] initWithBar:[[Bar alloc] init]]; // No error 
+0

Vielen Dank, das ist definitiv die bevorzugte Art und Weise. In meinem Fall muss _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ich Dies ist natürlich die allgemeine Lösung. Ich habe tatsächlich die Frage übersehen, die du verlinkt hast, weil ich init nicht "nur" privat machen wollte. Ich werde jetzt upvote. – Gero

Verwandte Themen