Clang dodaje słowo kluczowe, instancetype
które, o ile widzę, zastępuje id
jako typ zwracany w -alloc
i init
.
Czy istnieje korzyści stosując instancetype
zamiast id
?
objective-c
instancetype
griotspeak
źródło
źródło
id
, zostało zastąpionychinstancetype
, nawetinit
zNSObject
. Jeśli chcesz, aby kod był kompatybilny z szybkim, musisz użyćinstancetype
Odpowiedzi:
Na pewno jest korzyść. Kiedy używasz „id”, zasadniczo nie uzyskuje się żadnego sprawdzania typu. Dzięki instancetype kompilator i IDE wiedzą, jaki typ rzeczy jest zwracany, i mogą lepiej sprawdzać kod i lepiej autouzupełnianie.
Używaj go tylko tam, gdzie ma to sens (tj. Metoda, która zwraca instancję tej klasy); identyfikator jest nadal przydatny.
źródło
alloc
,init
itp. są automatycznie promowaneinstancetype
przez kompilator. Nie oznacza to, że nie ma korzyści; jest, ale to nie to.Tak, korzystanie z nich jest korzystne
instancetype
we wszystkich przypadkach, w których ma to zastosowanie. Wyjaśnię bardziej szczegółowo, ale zacznę od tego odważnego stwierdzenia: używaj,instancetype
gdy 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:
Aby temu zapobiec, przejdźmy dalej i wyjaśnij, dlaczego to dobry pomysł.
Po pierwsze, niektóre definicje:
W fabryce klasowej zawsze powinieneś używać
instancetype
. Kompilator nie automatycznie konwertowaćid
doinstancetype
. Toid
jest 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 anid
, kompilator nie jest pewien, jakiej klasywriteData:
jest wywoływana.Musisz obejść ten problem, używając:
lub:
Oczywiście lepszym rozwiązaniem jest zadeklarowanie
fileHandleWithStandardOutput
zwrotuinstancetype
. 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
NSFileHandle
dostarczawriteData:
tam. Istnieją również inne przykłady, takie jaklength
, która zwracaCGFloat
zUILayoutSupport
aleNSUInteger
zNSString
).Uwaga : odkąd to napisałem, nagłówki macOS zostały zmodyfikowane, aby zwracały
NSFileHandle
zamiastid
.W przypadku inicjatorów jest to bardziej skomplikowane. Po wpisaniu:
… Kompilator udaje, że zamiast tego wpisałeś:
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
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 an
instancetype
init
id
instancetype
init
id
instancetype
.Są one równoważne z kompilatorem:
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
init
i innych metod, nie ma różnicy jak najszybciej określić fabrykę klasy.Te dwa nie są równoważne:
Chcesz drugiej formy. Jeśli jesteś przyzwyczajony do pisania
instancetype
jako typ zwracany przez konstruktor, za każdym razem będziesz to robić poprawnie.Konsystencja
Wreszcie, wyobraź sobie, że połączysz to wszystko: potrzebujesz
init
funkcji, a także fabryki klasy.Jeśli używasz
id
doinit
, możesz skończyć z kodem jak poniżej:Ale jeśli użyjesz
instancetype
, otrzymasz to: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
instancetype
razie potrzeby.Przed napisaniem zwrotnej wiadomości powinieneś się wahać
id
. Zadaj sobie pytanie: czy to zwraca instancję tej klasy? Jeśli tak, to jestinstancetype
.Z pewnością są przypadki, w których musisz wrócić
id
, ale prawdopodobnie będziesz używaćinstancetype
znacznie częściej.źródło
instancetype
vsid
naprawdę nie jest decyzją stylu. Ostatnie zmiany wokółinstancetype
naprawdę wyjaśniają, że powinniśmy używaćinstancetype
w miejscach takich jak-init
„przykład mojej klasy”Powyższe odpowiedzi są więcej niż wystarczające, aby wyjaśnić to pytanie. Chciałbym tylko dodać przykład, który czytelnicy mogą zrozumieć, jeśli chodzi o kodowanie.
Klasa A
Klasa B
TestViewController.m
źródło
Możesz także uzyskać szczegółowe informacje w The Designated Initializer
**
INSTANCETYPE
** Tego słowa kluczowego można użyć tylko dla typu zwrotu, który jest zgodny z typem zwrotu odbiornika. Metoda init zawsze deklarowała, że zwraca typ instancet. Dlaczego na przykład nie ustawić typu zwrotu Party dla wystąpienia strony? To spowodowałoby problem, gdyby klasa Party kiedykolwiek została podklasowana. Podklasa dziedziczy wszystkie metody z Party, w tym inicjator i jego typ zwracany. Jeśli instancja podklasy zostanie wysłana do tego komunikatu inicjalizującego, to czy zostanie zwrócony? Nie wskaźnik do instancji Party, ale wskaźnik do instancji podklasy. Może ci się wydawać, że to żaden problem, zastąpię inicjalizator w podklasie, aby zmienić typ zwracany. Ale w Objective-C nie można mieć dwóch metod z tym samym selektorem i różnymi typami zwrotu (lub argumentami). Określając, że metoda inicjowania zwraca „
ID
** Przed wprowadzeniem typu instancive w Objective-C inicjalizatory zwracają id (eye-dee). Ten typ jest zdefiniowany jako „wskaźnik do dowolnego obiektu”. (id jest bardzo podobny do void * w C.) W tym piśmie szablony klas XCode nadal używają id jako zwracanego typu inicjalizatorów dodanych w kodzie podstawowym. W przeciwieństwie do instancetype, id może być użyty jako coś więcej niż tylko typ zwrotu. Możesz zadeklarować zmienne lub parametry metody typu id, gdy nie masz pewności, na jaki typ obiektu wskaże zmienna. Możesz użyć id, gdy używasz szybkiego wyliczania do iteracji w tablicy wielu lub nieznanych typów obiektów. Zauważ, że ponieważ id jest niezdefiniowany jako „wskaźnik do dowolnego obiektu”, nie podajesz * przy deklarowaniu zmiennej lub parametru obiektu tego typu.
źródło