Ich habe eine allgemeine Frage zum Schreiben von Init-Methoden in Objective-C.
Ich sehe überall (Apples Code, Bücher, Open Source Code usw.), dass eine Init-Methode prüfen sollte, ob self = [super init] nicht null ist, bevor sie mit der Initialisierung fortfährt.
Die Standardvorlage von Apple für eine Init-Methode lautet:
- (id) init
{
self = [super init];
if (self != nil)
{
// your code here
}
return self;
}
Warum?
Ich meine, wann wird init jemals null zurückgeben? Wenn ich init auf NSObject angerufen habe und nichts zurückbekommen habe, muss etwas wirklich durcheinander gebracht werden, oder? Und in diesem Fall könnten Sie genauso gut nicht einmal ein Programm schreiben ...
Ist es wirklich so üblich, dass die Init-Methode einer Klasse null zurückgibt? Wenn ja, in welchem Fall und warum?
objective-c
null
init
Jasarien
quelle
quelle
Antworten:
Beispielsweise:
... und so weiter. (Hinweis: NSData löst möglicherweise eine Ausnahme aus, wenn die Datei nicht vorhanden ist.) Es gibt einige Bereiche, in denen die Rückgabe von Null das erwartete Verhalten ist, wenn ein Problem auftritt, und aus diesem Grund ist es üblich, aus Gründen der Konsistenz fast immer auf Null zu prüfen.
quelle
Diese spezielle Redewendung ist Standard, da sie in allen Fällen funktioniert.
Es ist zwar ungewöhnlich, aber es wird Fälle geben, in denen ...
... gibt eine andere Instanz zurück und erfordert daher die Zuweisung zu self.
In einigen Fällen wird nil zurückgegeben, sodass die Prüfung nil erforderlich ist, damit Ihr Code nicht versucht, einen nicht mehr vorhandenen Instanzvariablen-Slot zu initialisieren.
Die Quintessenz ist, dass es das dokumentierte korrekte Muster ist, das verwendet werden soll, und wenn Sie es nicht verwenden, machen Sie es falsch.
quelle
-init
scheint ...)[super init]
Null zurückgegeben wird, wenn die direkte Oberklasse istNSObject
? Ist das nicht ein Fall, in dem "alles kaputt ist"?NSObject
es sich um die direkte Oberklasse handelt. Aber ... selbst wenn SieNSObject
als direkte Superklasse deklarieren , könnte zur Laufzeit etwas geändert worden sein, so dassNSObject
die Implementierung voninit
nicht das ist, was eigentlich genannt wird.Ich denke, in den meisten Klassen funktioniert Ihre App im Grunde immer noch nicht richtig, wenn der Rückgabewert von [super init] Null ist und Sie ihn überprüfen, wie von den Standardpraktiken empfohlen, und dann vorzeitig zurückgeben, wenn Null, dass Ihre App im Grunde immer noch nicht richtig funktioniert. Wenn man darüber nachdenkt, obwohl , dass , wenn (selbst! = Nil) Überprüfung ist es, für den ordnungsgemäßen Betrieb der Klasse, 99,99% der Zeit , die Sie tatsächlich tun müssen selbst Nicht-Null zu sein. Nehmen wir nun an, aus welchem Grund auch immer, [super init] hat nil zurückgegeben. Im Grunde geht Ihr Scheck gegen nil im Grunde genommen an den Anrufer Ihrer Klasse, wo er wahrscheinlich sowieso fehlschlagen würde, da er natürlich davon ausgeht, dass der Anruf war erfolgreich.
Grundsätzlich geht es mir darum, dass das if (self! = Nil) in 99,99% der Fälle nichts im Hinblick auf eine größere Robustheit kauft, da Sie nur das Geld an Ihren Anrufer weitergeben. Um dies wirklich robust handhaben zu können, müssten Sie tatsächlich Überprüfungen in Ihrer gesamten aufrufenden Hierarchie vornehmen. Und selbst dann würde es Ihnen nur kaufen, wenn Ihre App etwas sauberer / robuster ausfällt. Aber es würde immer noch scheitern.
Wenn eine Bibliotheksklasse aufgrund eines [Super-Init] willkürlich beschlossen hat, Null zurückzugeben, sind Sie sowieso ziemlich verärgert, und dies ist eher ein Hinweis darauf, dass der Verfasser der Bibliotheksklasse einen Fehler bei der Implementierung gemacht hat.
Ich denke, dies ist eher ein Legacy-Codierungsvorschlag, wenn Apps in einem viel begrenzteren Speicher ausgeführt wurden.
Aber für Code auf C-Ebene würde ich normalerweise immer noch den Rückgabewert von malloc () gegen einen NULL-Zeiger prüfen. Während ich für Objective-C, bis ich Beweise für das Gegenteil finde, im Allgemeinen die if (self! = Nil) -Prüfungen überspringe. Warum die Diskrepanz?
Denn auf der C- und Malloc-Ebene können Sie sich in einigen Fällen tatsächlich teilweise erholen. Während ich in Objective-C denke, dass in 99,99% der Fälle, wenn [super init] null zurückgibt, Sie im Grunde genommen gefickt sind, selbst wenn Sie versuchen, damit umzugehen. Sie können die App auch einfach abstürzen lassen und sich mit den Folgen befassen.
quelle
Dies ist eine Art Zusammenfassung der obigen Kommentare.
Nehmen wir an, die Oberklasse kehrt zurück
nil
. Was ist passiert?Wenn Sie die Konventionen nicht befolgen
Ihr Code wird mitten in Ihrer
init
Methode abstürzen . (es sei denninit
, nichts von Bedeutung)Wenn Sie die Konventionen befolgen und nicht wissen, dass die Oberklasse möglicherweise Null zurückgibt (die meisten Leute landen hier)
Ihr Code wird wahrscheinlich irgendwann später abstürzen, weil Sie in Ihrer Instanz
nil
etwas anderes erwartet haben. Oder Ihr Programm verhält sich unerwartet, ohne abzustürzen. Ach je! Willst du das? Ich weiß es nicht...Wenn Sie die Konventionen befolgen, lassen Sie Ihre Unterklasse bereitwillig Null zurückgeben
In Ihrer Codedokumentation (!) Sollte eindeutig angegeben sein: "return ... oder nil", und der Rest Ihres Codes muss darauf vorbereitet sein. Jetzt macht es Sinn.
quelle
Wenn Ihre Klasse direkt von abgeleitet ist
NSObject
, müssen Sie dies normalerweise nicht tun. Es ist jedoch eine gute Angewohnheit, sich darauf einzulassen, als ob Ihre Klasse von anderen Klassen abgeleitet wäre, deren Initialisierer möglicherweise zurückkehrennil
. Wenn dies der Fall ist, kann Ihr Initialisierer dies erfassen und sich korrekt verhalten.Und ja, fürs Protokoll, ich folge den Best Practices und schreibe sie in alle meine Klassen, auch in diejenigen, die direkt von ihnen stammen
NSObject
.quelle
Foo *bar = [[Foo alloc] init]; if (bar) {[bar doStuff];}
NSObject
garantiert nicht, dass-init
es IhnenNSObject
auch gibt , wenn Sie exotische Laufzeiten wie ältere Versionen von GNUstep in (die zurückkehrenGSObject
) zählen, egal was passiert , eine Prüfung und eine Zuweisung.Sie haben Recht, Sie könnten oft nur schreiben
[super init]
, aber das würde für eine Unterklasse von einfach nichts funktionieren. Die Leute bevorzugen es, sich nur eine Standard-Codezeile zu merken und sie die ganze Zeit zu verwenden, auch wenn dies nur manchmal notwendig ist, und so erhalten wir den Standardif (self = [super init])
, der sowohl die Möglichkeit der Rückgabe von Null als auch die Möglichkeit eines anderen Objekts alsself
der Rückgabe berücksichtigt berücksichtigen.quelle
Ein häufiger Fehler ist das Schreiben
Dies gibt eine Instanz der Oberklasse zurück, die NICHT das ist, was Sie in einem Unterklassenkonstruktor / init wollen. Sie erhalten ein Objekt zurück, das nicht auf die Unterklassenmethoden reagiert, was verwirrend sein kann, und verwirrende Fehler, wenn nicht auf nicht gefundene Methoden oder Bezeichner usw. geantwortet wird.
wird benötigt, wenn die Superklasse Mitglieder (Variablen oder andere Objekte) hat, die zuerst initialisiert werden müssen, bevor die Mitglieder der Unterklassen eingerichtet werden. Andernfalls initialisiert die objc-Laufzeit sie alle auf 0 oder null . (Im Gegensatz zu ANSI C, das häufig Speicherblöcke zuweist, ohne sie zu löschen )
Und ja, die Initialisierung der Basisklasse kann aufgrund von Fehlern aufgrund von Speichermangel, fehlenden Komponenten, Fehlern bei der Ressourcenerfassung usw. fehlschlagen. Eine Überprüfung auf Null ist daher sinnvoll und dauert weniger als einige Millisekunden.
quelle
Hiermit wird überprüft, ob die Intialazation funktioniert hat. Die if-Anweisung gibt true zurück, wenn die init-Methode nicht nil zurückgegeben hat. Auf diese Weise können Sie überprüfen, ob die Erstellung des Objekts ordnungsgemäß funktioniert hat. Nur wenige Gründe, warum ich mir vorstellen kann, dass dieser Init fehlschlägt, sind möglicherweise eine überschriebene Init-Methode, die die Superklasse nicht kennt, oder etwas Ähnliches. Ich würde jedoch nicht denken, dass dies so häufig vorkommt. Aber wenn es passiert, ist es besser, dass nichts passiert, als ein Absturz, den ich vermute, also wird es immer überprüft ...
quelle
In OS X ist es aus
-[NSObject init]
Speichergründen nicht so wahrscheinlich , dass ein Fehler auftritt. Das gilt nicht für iOS.Es ist auch eine gute Vorgehensweise zum Schreiben, wenn Sie eine Klasse unterordnen, die
nil
aus irgendeinem Grund zurückkehren kann.quelle
-[NSObject init]
ist es sehr unwahrscheinlich, dass aus Speichergründen ein Fehler auftritt, da kein Speicher zugewiesen wird.