Czy byłoby korzystne zacząć używać instancetype zamiast id?
Clang dodaje słowo kluczowe instancetype
to, o ile widzę, zastępuje id
jako typ powrotu w -alloc
i init
.
Czy jest korzyść z używania instancetype
zamiast id
?
4 answers
Na pewno jest korzyść. Kiedy używasz 'id', nie masz praktycznie żadnego sprawdzania typu. Dzięki instancetype kompilator i IDE wiedzą, jaki typ rzeczy jest zwracany, i mogą lepiej sprawdzić kod i lepiej autouzupełniać.
Używaj go tylko tam, gdzie ma to oczywiście sens (tj. metoda zwracająca instancję tej klasy); id jest nadal użyteczne.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-01-23 18:47:01
Tak, istnieją korzyści z używania instancetype
we wszystkich przypadkach, w których ma to zastosowanie. Wyjaśnię to bardziej szczegółowo, ale zacznę od tego odważnego stwierdzenia: używaj instancetype
, gdy jest to właściwe, czyli gdy Klasa zwraca instancję tej samej klasy.
W kodzie zastąp wystąpienia
id
jako wartość zwracaną przezinstancetype
, jeśli jest to właściwe. Zazwyczaj tak jest w przypadku metodinit
i klasy factory metody. Mimo że kompilator automatycznie konwertuje metody, które zaczynają się od "alloc", "init" lub " new " i mają typ zwracaniaid
na returninstancetype
, nie konwertuje innych metod. Konwencja Objective-C jest zapiseminstancetype
jawnie dla wszystkich metod.
-
/ Align = "left" / Źródło: Przyjęcie Nowoczesnego Celu-C
Z tym na uboczu, przejdźmy dalej i wyjaśnijmy, dlaczego to dobry pomysł.
Po pierwsze, niektóre definicje:
@interface Foo:NSObject
- (id)initWithBar:(NSInteger)bar; // initializer
+ (id)fooWithBar:(NSInteger)bar; // class factory
@end
Dla fabryki klas, powinieneś zawsze używać instancetype
. Kompilator nie konwertuje automatycznie id
na instancetype
. Że {[11] } jest obiektem ogólnym. Ale jeśli uczynisz go instancetype
kompilator wie, jakiego typu obiekt zwraca metoda.
To jestNie problem akademicki. Na przykład, [[NSFileHandle fileHandleWithStandardOutput] writeData:formattedData]
wygeneruje błąd tylko w systemie Mac OS X () znaleziono wiele metod o nazwie 'writeData:' z niedopasowanym wynikiem, typem parametru lub atrybuty . Powodem jest to, że zarówno NSFileHandle, jak i NSURLHandle zapewniają writeData:
. Ponieważ [NSFileHandle fileHandleWithStandardOutput]
zwraca id
, kompilator nie jest pewien, która klasa writeData:
jest wywoływana.
Musisz obejść to, używając:
[(NSFileHandle *)[NSFileHandle fileHandleWithStandardOutput] writeData:formattedData];
Lub:
NSFileHandle *fileHandle = [NSFileHandle fileHandleWithStandardOutput];
[fileHandle writeData:formattedData];
Oczywiście lepszym rozwiązaniem jest zadeklarowanie fileHandleWithStandardOutput
jako zwracającego instancetype
. Wtedy Obsada lub zadanie nie jest konieczne.
writeData:
tam. Istnieją inne przykłady, takie jak length
, które zwraca CGFloat
Z UILayoutSupport
, ale NSUInteger
z NSString
.)
Uwaga: Od kiedy to napisałem, nagłówki macOS zostały zmodyfikowane tak, aby zwracały NSFileHandle
zamiast id
.
W przypadku inicjalizatorów jest to bardziej skomplikowane. Po wpisaniu tego:
- (id)initWithBar:(NSInteger)bar
...kompilator będzie udawał, że wpisałeś to zamiast:
- (instancetype)initWithBar:(NSInteger)bar
To było konieczne dla ARC. Jest to opisane w języku Clang Rozszerzenia powiązane typy wyników . Dlatego ludzie powiedzą ci, że nie trzeba używać instancetype
, chociaż uważam, że powinieneś. Reszta odpowiedzi dotyczy tego.
Są trzy zalety:
- 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ą.
- spójność. ustaliłeś jakieś spójność z Twoim kodem, co czyni go bardziej czytelnym.
Explicit
To prawda, że nie matechnicznych korzyści z powrotu instancetype
z init
. Jest to spowodowane tym, że kompilator automatycznie konwertuje id
na instancetype
. Polegasz na tym dziwactwie; kiedy piszesz, że init
zwraca id
, kompilator interpretuje to tak, jakby zwracał instancetype
.
Są to równoważne do kompilator:
- (id)initWithBar:(NSInteger)bar;
- (instancetype)initWithBar:(NSInteger)bar;
Te nie są równoważne Twoim oczom. W najlepszym razie nauczysz się ignorować różnicę i przejrzeć ją. to nie jest coś, czego powinieneś nauczyć się ignorować.
Wzór
Podczas gdy nie ma różnicy z init
i innymi metodami, istnieje jest różnica, gdy tylko zdefiniujesz fabrykę klas.
Te dwa nie są równoważne:
+ (id)fooWithBar:(NSInteger)bar;
+ (instancetype)fooWithBar:(NSInteger)bar;
Chcesz drugiej formy. Jeśli jesteś przyzwyczajony do pisania instancetype
jako return type of a constructor, you ' ll get it right every time.
Spójność
Wreszcie, wyobraź sobie, że poskładasz to wszystko do kupy: chcesz init
funkcji, a także fabryki klas.
Jeśli używasz id
do init
, kończysz z kodem takim jak:
- (id)initWithBar:(NSInteger)bar;
+ (instancetype)fooWithBar:(NSInteger)bar;
Ale jeśli użyjesz instancetype
, dostaniesz to:
- (instancetype)initWithBar:(NSInteger)bar;
+ (instancetype)fooWithBar:(NSInteger)bar;
Jest bardziej spójny i czytelny. Zwracają to samo, a teraz to oczywiste.
Podsumowanie
Chyba, że jesteś celowo pisząc kod dla starych kompilatorów, powinieneś użyć instancetype
, gdy jest to właściwe.
Powinieneś zawahać się przed napisaniem wiadomości, która zwróci id
. Zadaj sobie pytanie: czy to zwrócenie jest instancją tej klasy? Jeśli tak, to jest to instancetype
.
Z pewnością są przypadki, w których musisz zwrócić id
, ale prawdopodobnie będziesz używać instancetype
znacznie częściej.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-25 18:30:48
Powyższe odpowiedzi są więcej niż wystarczające, aby wyjaśnić to pytanie. Chciałbym tylko dodać przykład dla czytelników, aby zrozumieli go w kategoriach kodowania.
ClassA
@interface ClassA : NSObject
- (id)methodA;
- (instancetype)methodB;
@end
Klasa 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
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-18 18:33:36
Możesz również uzyskać szczegóły w wyznaczonym Inicjalizatorze
**
INSTANCETYPE
** To słowo kluczowe może być używane tylko dla typu return, że pasuje do typu return odbiornika. metoda init zawsze deklarowana jako zwracająca instancetype. Dlaczego na przykład nie uczynić typu Return Party dla instancji party? To sprawiłoby problem, gdyby Klasa partyjna była kiedykolwiek podklasowana. Podklasa dziedziczy wszystkie metody ze strony, w tym inicjalizator i jego powrót Typ. Jeśli instancja podklasy została wysłana tym komunikatem inicjującym, to będzie to return? Nie wskaźnik do instancji Party, ale wskaźnik do instancji podklasy. Możesz pomyśleć, że to nie problem, nadpiszę inicjalizator w podklasie, aby zmienić typ powrotu. Ale w Objective-C nie można mieć dwóch metod z tym samym selektorem i różnymi typami zwrotów (lub argumentami). Określając, że metoda inicjalizacji zwraca "instancję obiektu odbierającego", można nigdy nie musisz się martwić, co dzieje się w tej sytuacji. **
ID
** Przed wprowadzeniem typu instancetype w Objective-C, inicjalizatory zwracają id (eye-dee). Ten typ jest zdefiniowany jako "wskaźnik do dowolnego obiektu". (id jest bardzo podobne do void * W C.) W chwili pisania tego tekstu, szablony klas Xcode nadal używają id jako zwracanego typu inicjalizatorów dodanych w kodzie boilerplate. W przeciwieństwie do instancetype, id może być używane jako coś więcej niż tylko Typ zwrotu. Możesz zadeklarować zmienne lub parametry metody wpisz id, gdy nie masz pewności, do jakiego typu obiektu będzie wskazywana zmienna. Można użyć id, gdy używa się szybkiego wyliczenia do iteracji na tablicy wielu lub nieznanych typów obiektów. Zauważ, że ponieważ id jest niezdefiniowany jako "wskaźnik do dowolnego obiektu", nie uwzględniasz * deklarując zmienną lub parametr obiektu tego typu.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-23 08:23:32