Clang fügt ein Schlüsselwort hinzu instancetype
, das, soweit ich sehen kann, id
als Rückgabetyp in -alloc
und ersetzt init
.
Gibt es einen Vorteil instancetype
anstelle von id
?
objective-c
instancetype
Griotspeak
quelle
quelle
id
wurden, durch ersetzt wurdeninstancetype
, sogarinit
vonNSObject
. Wenn Sie Ihren Code mit Swift kompatibel machen möchten, müssen Sieinstancetype
Antworten:
Es gibt definitiv einen Vorteil. Wenn Sie 'id' verwenden, erhalten Sie im Wesentlichen überhaupt keine Typprüfung. Mit instancetype wissen der Compiler und die IDE, welche Art von Dingen zurückgegeben wird, und können Ihren Code besser überprüfen und besser automatisch vervollständigen.
Verwenden Sie es nur dort, wo es natürlich Sinn macht (dh eine Methode, die eine Instanz dieser Klasse zurückgibt). ID ist immer noch nützlich.
quelle
alloc
,init
usw. werdeninstancetype
vom Compiler automatisch heraufgestuft. Das heißt nicht, dass es keinen Nutzen gibt; gibt es, aber das ist es nicht.Ja, die Verwendung bietet Vorteile
instancetype
in allen Fällen, in denen sie gilt. Ich werde es genauer erklären, aber lassen Sie mich mit dieser kühnen Aussage beginnen: Verwenden Sie sie,instancetype
wann immer es angebracht ist, dh wann immer eine Klasse eine Instanz derselben Klasse zurückgibt.In der Tat ist hier, was Apple jetzt zu diesem Thema sagt:
Lassen Sie uns weitermachen und erklären, warum dies eine gute Idee ist.
Zunächst einige Definitionen:
Für eine Klassenfabrik sollten Sie immer verwenden
instancetype
. Der Compiler konvertiert nicht automatischid
ininstancetype
. Dasid
ist ein generisches Objekt. Wenn Sie es jedoch zu eineminstancetype
Objekt machen, weiß der Compiler, welchen Objekttyp die Methode zurückgibt.Dies ist kein akademisches Problem. Beispielsweise
[[NSFileHandle fileHandleWithStandardOutput] writeData:formattedData]
wird unter Mac OS X ( nur ) ein Fehler generiert. Mehrere Methoden mit dem Namen 'writeData:' wurden mit nicht übereinstimmendem Ergebnis, Parametertyp oder Attributen gefunden . Der Grund ist, dass sowohl NSFileHandle als auch NSURLHandle a bereitstellenwriteData:
. Da der Compiler[NSFileHandle fileHandleWithStandardOutput]
ein zurückgibtid
, ist er nicht sicher, welche KlassewriteData:
aufgerufen wird.Sie müssen dies umgehen, indem Sie entweder Folgendes verwenden:
oder:
Die bessere Lösung besteht natürlich darin,
fileHandleWithStandardOutput
eine Rückgabe zu deklariereninstancetype
. Dann ist die Besetzung oder Aufgabe nicht notwendig.(Beachten Sie, dass dieses Beispiel unter iOS keinen Fehler erzeugt, da nur
NSFileHandle
einwriteData:
dort angegeben wird. Es gibt andere Beispiele, z. B.length
, die einCGFloat
vonUILayoutSupport
aber einNSUInteger
von zurückgebenNSString
.)Hinweis : Seit ich dies geschrieben habe, wurden die macOS-Header so geändert, dass sie a
NSFileHandle
anstelle von a zurückgebenid
.Für Initialisierer ist es komplizierter. Wenn Sie dies eingeben:
… Der Compiler wird so tun, als hätten Sie dies stattdessen eingegeben:
Dies war für ARC notwendig. Dies wird unter Clang Language Extensions Related-Ergebnistypen beschrieben . Dies ist der Grund, warum die Leute Ihnen sagen werden, dass es nicht notwendig ist, es zu verwenden
instancetype
, obwohl ich der Meinung bin, dass Sie es sollten. Der Rest dieser Antwort befasst sich damit.Es gibt drei Vorteile:
Explizit
Es ist wahr, dass es keinen technischen Vorteil gibt,
instancetype
von einem zurückzukehreninit
. Aber das ist , weil der Compiler automatisch die konvertiertid
zuinstancetype
. Sie verlassen sich auf diese Eigenart; Während Sie schreiben, dass das eininit
zurückgibtid
, interpretiert der Compiler es so, als ob es ein zurückgibtinstancetype
.Diese entsprechen dem Compiler:
Diese entsprechen nicht Ihren Augen. Bestenfalls werden Sie lernen, den Unterschied zu ignorieren und ihn zu überfliegen. Dies sollten Sie nicht ignorieren lernen.
Muster
Während es keinen Unterschied zu
init
und anderen Methoden gibt, gibt es einen Unterschied, sobald Sie eine Klassenfactory definieren.Diese beiden sind nicht gleichwertig:
Sie möchten das zweite Formular. Wenn Sie es gewohnt sind,
instancetype
als Rückgabetyp eines Konstruktors zu tippen, werden Sie es jedes Mal richtig machen.Konsistenz
Stellen Sie sich zum Schluss vor, Sie setzen alles zusammen: Sie wollen eine
init
Funktion und auch eine Klassenfabrik.Wenn Sie
id
für verwendeninit
, erhalten Sie Code wie folgt:Aber wenn Sie verwenden
instancetype
, erhalten Sie Folgendes:Es ist konsistenter und lesbarer. Sie geben dasselbe zurück, und das ist jetzt offensichtlich.
Fazit
Sofern Sie nicht absichtlich Code für alte Compiler schreiben, sollten Sie diesen
instancetype
gegebenenfalls verwenden.Sie sollten zögern, bevor Sie eine Nachricht schreiben, die zurückkehrt
id
. Fragen Sie sich: Gibt dies eine Instanz dieser Klasse zurück? Wenn ja, ist es eininstancetype
.Es gibt sicherlich Fälle, in denen Sie zurückkehren müssen
id
, aber Sie werden wahrscheinlichinstancetype
viel häufiger verwenden.quelle
instancetype
vs istid
wirklich keine Stilentscheidung. Die jüngsten Änderungeninstancetype
machen wirklich deutlich, dass wir sieinstancetype
an Orten verwenden sollten, an-init
denen wir "eine Instanz meiner Klasse" meinenDie obigen Antworten sind mehr als genug, um diese Frage zu erklären. Ich möchte nur ein Beispiel hinzufügen, damit die Leser es in Bezug auf die Codierung verstehen.
Klasse a
Klasse b
TestViewController.m
quelle
Details erhalten Sie auch unter The Designated Initializer
** **.
INSTANZETYP
** Dieses Schlüsselwort kann nur für den Rückgabetyp verwendet werden, der mit dem Rückgabetyp des Empfängers übereinstimmt. Die init-Methode wird immer als Rückgabetyp deklariert. Warum nicht zum Beispiel den Rückgabetyp Party für Party-Instanz festlegen? Das würde ein Problem verursachen, wenn die Parteiklasse jemals untergeordnet würde. Die Unterklasse würde alle Methoden von Party erben, einschließlich des Initialisierers und seines Rückgabetyps. Wenn eine Instanz der Unterklasse diese Initialisierungsnachricht gesendet hätte, wäre dies eine Rückgabe? Kein Zeiger auf eine Party-Instanz, sondern ein Zeiger auf eine Instanz der Unterklasse. Sie könnten denken, dass dies kein Problem ist. Ich werde den Initialisierer in der Unterklasse überschreiben, um den Rückgabetyp zu ändern. In Objective-C können Sie jedoch nicht zwei Methoden mit demselben Selektor und unterschiedlichen Rückgabetypen (oder Argumenten) verwenden. Durch Angabe, dass eine Initialisierungsmethode "
ICH WÜRDE
** Bevor der Instanztyp in Objective-C eingeführt wurde, geben Initialisierer die ID (eye-dee) zurück. Dieser Typ ist als "Zeiger auf ein beliebiges Objekt" definiert. (id ähnelt void * in C.) Zum jetzigen Zeitpunkt verwenden XCode-Klassenvorlagen immer noch id als Rückgabetyp für Initialisierer, die im Boilerplate-Code hinzugefügt wurden. Im Gegensatz zu instancetype kann id nicht nur als Rückgabetyp verwendet werden. Sie können Variablen oder Methodenparameter vom Typ id deklarieren, wenn Sie nicht sicher sind, auf welchen Objekttyp die Variable am Ende verweist. Sie können id verwenden, wenn Sie eine schnelle Aufzählung verwenden, um über ein Array mehrerer oder unbekannter Objekttypen zu iterieren. Beachten Sie, dass Sie beim Deklarieren einer Variablen oder eines Objektparameters dieses Typs kein * einfügen, da id nicht als "Zeiger auf ein Objekt" definiert ist.
quelle