Tak, korzystanie z nich jest korzystne instancetypewe wszystkich przypadkach, w których ma to zastosowanie. Wyjaśnię bardziej szczegółowo, ale zacznę od tego odważnego stwierdzenia: używaj, instancetypegdy jest to właściwe, czyli za każdym razem, gdy klasa zwraca instancję tej samej klasy.
Oto, co Apple teraz mówi na ten temat:
W swoim kodzie zamień wystąpienia idjako wartość zwracaną na instancetypeodpowiednią. Zazwyczaj dzieje się tak w przypadku initmetod i metod fabrycznych klas. Mimo że kompilator automatycznie konwertuje metody zaczynające się od „przydzielania”, „inicjowania” lub „nowego” i ma typ idzwracany do zwrócenia instancetype, nie konwertuje innych metod. Konwencja celu C polega na instancetypejawnym pisaniu dla wszystkich metod.
Aby temu zapobiec, przejdźmy dalej i wyjaśnij, dlaczego to dobry pomysł.
Po pierwsze, niektóre definicje:
@interface Foo:NSObject
- (id)initWithBar:(NSInteger)bar; // initializer
+ (id)fooWithBar:(NSInteger)bar; // class factory
@end
W fabryce klasowej zawsze powinieneś używać instancetype. Kompilator nie automatycznie konwertować iddo instancetype. To idjest ogólny obiekt. Ale jeśli to zrobiszinstancetype kompilator wie, jaki typ obiektu zwraca metoda.
To nie jest problem akademicki. Na przykład [[NSFileHandle fileHandleWithStandardOutput] writeData:formattedData]wygeneruje błąd w systemie Mac OS X ( tylko ) Znaleziono wiele metod o nazwie „writeData:” z niedopasowanym wynikiem, typem parametru lub atrybutami . Powodem jest to, że zarówno NSFileHandle, jak i NSURLHandle zapewniają writeData:. Ponieważ [NSFileHandle fileHandleWithStandardOutput]zwraca an id, kompilator nie jest pewien, jakiej klasywriteData: jest wywoływana.
Musisz obejść ten problem, używając:
[(NSFileHandle *)[NSFileHandle fileHandleWithStandardOutput] writeData:formattedData];
lub:
NSFileHandle *fileHandle = [NSFileHandle fileHandleWithStandardOutput];
[fileHandle writeData:formattedData];
Oczywiście lepszym rozwiązaniem jest zadeklarowanie fileHandleWithStandardOutputzwrotuinstancetype . Wtedy obsada lub zadanie nie jest konieczne.
(Należy pamiętać, że na iOS, w tym przykładzie nie będzie produkować błąd jak tylko NSFileHandledostarcza writeData:tam. Istnieją również inne przykłady, takie jak length, która zwraca CGFloatz UILayoutSupportale NSUIntegerz NSString).
Uwaga : odkąd to napisałem, nagłówki macOS zostały zmodyfikowane, aby zwracały NSFileHandlezamiastid .
W przypadku inicjatorów jest to bardziej skomplikowane. Po wpisaniu:
- (id)initWithBar:(NSInteger)bar
… Kompilator udaje, że zamiast tego wpisałeś:
- (instancetype)initWithBar:(NSInteger)bar
Było to konieczne dla ARC. Jest to opisane w Powiązane typy rozszerzeń języka Clang . Dlatego ludzie powiedzą ci, że nie musisz używaćinstancetype , chociaż uważam, że powinieneś. Reszta tej odpowiedzi dotyczy tego.
Istnieją trzy zalety:
- Wyraźny. Twój kod robi to, co mówi, a nie coś innego.
- Wzór. Budujesz dobre nawyki na czasy, które mają znaczenie, które istnieją.
- Konsystencja. Ustaliłeś pewną spójność kodu, dzięki czemu jest on bardziej czytelny.
Wyraźny
To prawda, że powrót z nie ma żadnych technicznych korzyści . Ale to dlatego, że kompilator automatycznie konwertuje TO . Polegasz na tym dziwactwie; podczas gdy piszesz, że zwraca an , kompilator interpretuje to tak, jakby zwraca aninstancetypeinitidinstancetypeinitidinstancetype .
Są one równoważne z kompilatorem:
- (id)initWithBar:(NSInteger)bar;
- (instancetype)initWithBar:(NSInteger)bar;
To nie jest równoznaczne z twoimi oczami. W najlepszym przypadku nauczysz się ignorować różnicę i przejrzeć ją. To nie jest coś, co powinieneś nauczyć się ignorować.
Wzór
Chociaż nie ma różnicy z initi innych metod, nie ma różnicy jak najszybciej określić fabrykę klasy.
Te dwa nie są równoważne:
+ (id)fooWithBar:(NSInteger)bar;
+ (instancetype)fooWithBar:(NSInteger)bar;
Chcesz drugiej formy. Jeśli jesteś przyzwyczajony do pisania instancetypejako typ zwracany przez konstruktor, za każdym razem będziesz to robić poprawnie.
Konsystencja
Wreszcie, wyobraź sobie, że połączysz to wszystko: potrzebujesz initfunkcji, a także fabryki klasy.
Jeśli używasz iddo init, możesz skończyć z kodem jak poniżej:
- (id)initWithBar:(NSInteger)bar;
+ (instancetype)fooWithBar:(NSInteger)bar;
Ale jeśli użyjesz instancetype, otrzymasz to:
- (instancetype)initWithBar:(NSInteger)bar;
+ (instancetype)fooWithBar:(NSInteger)bar;
Jest bardziej spójny i bardziej czytelny. Zwracają to samo, a teraz to oczywiste.
Wniosek
O ile celowo nie piszesz kodu dla starych kompilatorów, powinieneś go używać w instancetyperazie potrzeby.
Przed napisaniem zwrotnej wiadomości powinieneś się wahać id. Zadaj sobie pytanie: czy to zwraca instancję tej klasy? Jeśli tak, to jest instancetype.
Z pewnością są przypadki, w których musisz wrócić id, ale prawdopodobnie będziesz używać instancetypeznacznie częściej.