Domanda

Clang aggiunge una parola chiave instancetype Questo, per quanto posso vedere, sostituisce id Come tipo di ritorno -alloc e init.

C'è un vantaggio da utilizzare instancetype invece di id?

È stato utile?

Soluzione

C'è sicuramente un vantaggio. Quando usi 'ID', non si ottiene essenzialmente nessun controllo di tipo. Con InstanceType, il compilatore e l'IDE sanno quale tipo di cose viene restituito e possono controllare meglio il codice e migliorare automaticamente.

Usalo solo dove ha senso ovviamente (cioè un metodo che sta restituendo un'istanza di quella classe); Id è ancora utile.

Altri suggerimenti

Sì, ci sono vantaggi da utilizzare instancetype In tutti i casi in cui si applica. Spiegherò in modo più dettagliato, ma lasciami iniziare con questa audace affermazione: usa instancetype Ogni volta che è appropriato, che è ogni volta che una classe restituisce un'istanza di quella stessa classe.

In effetti, ecco cosa dice Apple ora sull'argomento:

Nel codice, sostituire gli occorrenze di id come valore di ritorno con instancetype ove opportuno. Questo è in genere il caso per init Metodi e metodi di fabbrica di classe. Anche se il compilatore converte automaticamente i metodi che iniziano con "alloc", "init" o "nuovo" e hanno un tipo di ritorno di restituzione id ritornare instancetype, non convertisce altri metodi. L'obiettivo-C Convention è scrivere instancetype esplicitamente per tutti i metodi.

Con ciò, andiamo avanti e spieghiamo perché è una buona idea.

Innanzitutto, alcune definizioni:

 @interface Foo:NSObject
 - (id)initWithBar:(NSInteger)bar; // initializer
 + (id)fooWithBar:(NSInteger)bar;  // class factory
 @end

Per una fabbrica di classe, dovresti sempre uso instancetype. Il compilatore non converte automaticamente id a instancetype. Quella id è un oggetto generico. Ma se lo fai un instancetype Il compilatore sa che tipo di oggetto restituisce il metodo.

Questo è non un problema accademico. Per esempio, [[NSFileHandle fileHandleWithStandardOutput] writeData:formattedData] genererà un errore su Mac OS X (solo) Metodi multipli denominati "WriteData:" trovati con risultato non corrispondente, tipo di parametro o attributi. Il motivo è che sia NsfileHandle che NsurlHandle forniscono un writeData:. Da [NSFileHandle fileHandleWithStandardOutput] Restituisce un id, il compilatore non è sicuro di quale classe writeData: viene chiamato.

Devi aggirare questo, usando entrambi:

[(NSFileHandle *)[NSFileHandle fileHandleWithStandardOutput] writeData:formattedData];

o:

NSFileHandle *fileHandle = [NSFileHandle fileHandleWithStandardOutput];
[fileHandle writeData:formattedData];

Naturalmente, la soluzione migliore è dichiarare fileHandleWithStandardOutput come restituire un instancetype. Quindi il cast o il compito non sono necessari.

(Nota che su iOS, questo esempio non produrrà un errore come solo NSFileHandle fornisce un writeData: là. Esistono altri esempi, come length, che restituisce a CGFloat da UILayoutSupport ma a NSUInteger da NSString.)

Nota: Da quando ho scritto questo, le intestazioni macOS sono state modificate per restituire un NSFileHandle invece di un id.

Per gli inizializzatori, è più complicato. Quando digiti questo:

- (id)initWithBar:(NSInteger)bar

... Il compilatore fingerà di averlo digitato invece:

- (instancetype)initWithBar:(NSInteger)bar

Questo era necessario per l'arco. Questo è descritto nelle estensioni del linguaggio clango Tipi di risultati correlati. Questo è il motivo per cui le persone ti diranno che non è necessario usare instancetype, anche se sostengo che dovresti. Il resto di questa risposta si occupa di questo.

Ci sono tre vantaggi:

  1. Esplicito. Il tuo codice sta facendo quello che dice, piuttosto che qualcos'altro.
  2. Modello. Stai costruendo buone abitudini per i momenti, che esiste.
  3. Consistenza. Hai stabilito una certa coerenza al tuo codice, il che lo rende più leggibile.

Esplicito

È vero che non c'è no tecnico beneficiare del ritorno instancetype da un init. Ma questo perché il compilatore converte automaticamente il id a instancetype. Stai facendo affidamento su questa stranezza; Mentre stai scrivendo quello init Restituisce un id, il compilatore lo sta interpretando come se restituisca un instancetype.

Questi sono equivalente al compilatore:

- (id)initWithBar:(NSInteger)bar;
- (instancetype)initWithBar:(NSInteger)bar;

Questi non sono equivalenti ai tuoi occhi. Nella migliore delle ipotesi, imparerai a ignorare la differenza e a scambiarla. Questo non è qualcosa che dovresti imparare a ignorare.

Modello

Mentre non c'è differenza con init e altri metodi, lì è una differenza non appena definisci una fabbrica di classe.

Questi due non sono equivalenti:

+ (id)fooWithBar:(NSInteger)bar;
+ (instancetype)fooWithBar:(NSInteger)bar;

Vuoi la seconda forma. Se sei abituato a digitare instancetype Come tipo di ritorno di un costruttore, lo capirai ogni volta.

Consistenza

Infine, immagina se metti tutto insieme: vuoi un init funzione e anche una fabbrica di classe.

Se usi id per init, finisci con codice come questo:

- (id)initWithBar:(NSInteger)bar;
+ (instancetype)fooWithBar:(NSInteger)bar;

Ma se usi instancetype, ottieni questo:

- (instancetype)initWithBar:(NSInteger)bar;
+ (instancetype)fooWithBar:(NSInteger)bar;

È più coerente e più leggibile. Restituiscono la stessa cosa, e ora questo è ovvio.

Conclusione

A meno che tu non stia scrivendo intenzionalmente codice per vecchi compilatori, dovresti usare instancetype quando appropriato.

Dovresti esitare prima di scrivere un messaggio che ritorna id. Chiediti: questo sta restituendo un'istanza di questa classe? In tal caso, è un instancetype.

Ci sono certamente casi in cui devi tornare id, ma probabilmente lo userai instancetype molto più frequentemente.

Le risposte sopra sono più che sufficienti per spiegare questa domanda. Vorrei solo aggiungere un esempio per i lettori per capirlo in termini di codifica.

Classe A

@interface ClassA : NSObject

- (id)methodA;
- (instancetype)methodB;

@end

Classe B

@interface ClassB : NSObject

- (id)methodX;

@end

Testviewcontroller.m

#import "ClassA.h"
#import "ClassB.h"

- (void)viewDidLoad {

    [[[[ClassA alloc] init] methodA] methodX]; //This will NOT generate a compiler warning or error because the return type for methodA is id. Eventually this will generate exception at runtime

    [[[[ClassA alloc] init] methodB] methodX]; //This will generate a compiler error saying "No visible @interface ClassA declares selector methodX" because the methodB returns instanceType i.e. the type of the receiver
}

Puoi anche ottenere dettagli a L'inizializzatore designato

**

Instancetype

** Questa parola chiave può essere utilizzata solo per il tipo di restituzione, che corrisponde al tipo di restituzione del ricevitore. Metodo INIT ha sempre dichiarato di restituire InstanceType. Perché non rendere la festa del tipo di ritorno per l'istanza della festa, per esempio? Ciò causerebbe un problema se la classe del partito fosse mai sottoclassizzata. La sottoclasse erediterebbe tutti i metodi della festa, incluso l'inizializzatore e il suo tipo di ritorno. Se un'istanza della sottoclasse fosse inviata questo messaggio di inizializzatore, sarebbe reso? Non è un puntatore a un'istanza di partito, ma un puntatore a un'istanza di sottoclasse. Potresti pensare che non sia un problema, sovrascriverò l'inizializzatore nella sottoclasse per modificare il tipo di ritorno. Ma in Objective-C, non è possibile avere due metodi con lo stesso selettore e diversi tipi di ritorno (o argomenti). Specificando che un metodo di inizializzazione restituisce "un'istanza dell'oggetto ricevente", non dovresti mai preoccuparti di ciò che accade in questa situazione. **

ID

** Prima che l'InstanceType sia stato introdotto in Objective-C, Initializzar Return ID (Eye-Dee). Questo tipo è definito come "un puntatore a qualsiasi oggetto". (ID è molto simile a void * in C.) Al momento della stesura di questo documento, i modelli di classe Xcode usano ancora ID come tipo di restituzione degli inizializzatori aggiunti nel codice della placca a boiler. A differenza di InstanceType, l'ID può essere utilizzato come più di un semplice tipo di ritorno. È possibile dichiarare variabili o parametri del metodo dell'ID tipo quando non si è sicuri a quale tipo di oggetto la variabile finirà per indicare. È possibile utilizzare ID quando si utilizza un enumerazione rapida per iterare su una matrice di più tipi di oggetti. Si noti che perché ID è indefinito come "un puntatore a qualsiasi oggetto", non si include un * quando si dichiara un parametro variabile o oggetto di questo tipo.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top