Odpowiedzi:
void *
oznacza "odniesienie do jakiejś losowej porcji pamięci z nietypową / nieznaną zawartością"
id
oznacza „odniesienie do jakiegoś losowego obiektu Objective-C o nieznanej klasie”
Istnieją dodatkowe różnice semantyczne:
W trybach GC Only lub GC Supported kompilator wyemituje bariery zapisu dla odwołań typu id
, ale nie dla typu void *
. Podczas deklarowania struktur może to być krytyczna różnica. Zadeklarowanie iVars podobnych do void *_superPrivateDoNotTouch;
spowoduje przedwczesne zbieranie obiektów, jeśli _superPrivateDoNotTouch
faktycznie jest to obiekt. Nie rób tego.
próba wywołania metody na referencji void *
typu spowoduje wyświetlenie ostrzeżenia kompilatora.
próba wywołania metody dla id
typu spowoduje ostrzeżenie tylko wtedy, gdy wywoływana metoda nie została zadeklarowana w żadnej z @interface
deklaracji widzianych przez kompilator.
Dlatego nigdy nie należy odnosić się do obiektu jako do void *
. Podobnie, należy unikać używania id
zmiennej wpisanej w celu odniesienia się do obiektu. Użyj możliwie najbardziej szczegółowego odwołania do klasy. Nawet NSObject *
jest lepsze niż id
to, że kompilator może przynajmniej zapewnić lepszą walidację wywołań metod względem tego odwołania.
Jedynym typowym i prawidłowym zastosowaniem void *
jest nieprzezroczyste odniesienie do danych, które jest przesyłane przez inny interfejs API.
Rozważ sortedArrayUsingFunction: context:
metodę NSArray
:
- (NSArray *)sortedArrayUsingFunction:(NSInteger (*)(id, id, void *))comparator context:(void *)context;
Funkcja sortowania zostanie zadeklarowana jako:
NSInteger mySortFunc(id left, id right, void *context) { ...; }
W tym przypadku NSArray po prostu przekazuje jako context
argument do metody wszystko, co przekażesz jako context
argument. Jeśli chodzi o NSArray, jest to nieprzezroczysta porcja danych o rozmiarze wskaźnika, a możesz jej używać w dowolnym celu.
Bez funkcji typu zamknięcia w języku jest to jedyny sposób na przeniesienie porcji danych z funkcją. Przykład; jeśli chcesz, aby mySortFunc () warunkowo sortowało z uwzględnieniem wielkości liter lub bez rozróżniania wielkości liter, jednocześnie zachowując bezpieczeństwo wątków, przekazałbyś wskaźnik rozróżniający wielkość liter w kontekście, prawdopodobnie rzucając przy wejściu i wyjściu.
Kruchy i podatny na błędy, ale jedyny sposób.
Bloki rozwiązują ten problem - Bloki są zamknięciami dla C. Są dostępne w Clang - http://llvm.org/ i są wszechobecne w Snow Leopardzie ( http://developer.apple.com/library/ios/documentation/Performance /Reference/GCD_libdispatch_Ref/GCD_libdispatch_Ref.pdf ).
id
odpowiada an . An id
może łatwo odwołać się do instancji klasy, która nie pochodzi z NSObject
. Jednak praktycznie rzecz biorąc, twoje stwierdzenie najlepiej pasuje do zachowań w świecie rzeczywistym; nie możesz mieszać nie <NSObject>
implementujących klas z Foundation API i zajść bardzo daleko, to na pewno!
id
i Class
typy są traktowane jako utrzymywalny wskaźnik obiektu w ARC. Tak więc założenie jest prawdziwe przynajmniej w przypadku ARC.
id jest wskaźnikiem do obiektywnego obiektu C, gdzie as void * jest wskaźnikiem do czegokolwiek.
id wyłącza również ostrzeżenia związane z wywoływaniem nieznanych metod, a więc na przykład:
[(id)obj doSomethingWeirdYouveNeverHeardOf];
nie daje zwykłego ostrzeżenia o nieznanych metodach. Oczywiście zgłosi wyjątek w czasie wykonywania, chyba że obj jest nil lub naprawdę implementuje tę metodę.
Często powinieneś używać NSObject*
lub id<NSObject>
zamiast tego id
, co przynajmniej potwierdza, że zwrócony obiekt jest obiektem Cocoa, więc możesz bezpiecznie używać na nim metod takich jak retain / release / autorelease.
Often you should use NSObject*
zamiast tego id
. Określając NSObject*
, w rzeczywistości wyraźnie mówisz, że obiekt jest obiektem NSO. Każde wywołanie metody do obiektu spowoduje ostrzeżenie, ale nie będzie wyjątku w czasie wykonywania, dopóki ten obiekt rzeczywiście odpowie na wywołanie metody. Ostrzeżenie jest oczywiście denerwujące, więc id
jest lepsze. Z grubsza można wtedy być bardziej szczegółowym, na przykład mówiąc id<MKAnnotation>
, co w tym przypadku oznacza, że niezależnie od obiektu, musi on być zgodny z protokołem MKAnnotation.
Jeśli metoda ma zwracany typ id
, możesz zwrócić dowolny obiekt Objective-C.
void
oznacza, że metoda nic nie zwróci.
void *
jest tylko wskazówką. Nie będziesz w stanie edytować treści pod adresem wskazywanym przez wskaźnik.
id
jest wskaźnikiem do obiektu Objective-C. void *
jest wskaźnikiem do czegokolwiek . Możesz użyć void *
zamiast id
, ale nie jest to zalecane, ponieważ nigdy nie otrzymasz ostrzeżeń kompilatora o niczym.
Możesz zobaczyć stackoverflow.com/questions/466777/whats-the-difference-between-declaring-a-variable-id-and-nsobject i unixjunkie.blogspot.com/2008/03/id-vs-nsobject-vs -id.html .
void *
typowane zmienne z całą pewnością mogą być celem wywołań metod - to ostrzeżenie, a nie błąd. Nie tylko, że można to zrobić: int i = (int)@"Hello, string!";
a skontaktujemy się z: printf("Sending to an int: '%s'\n", [i UTF8String]);
. To ostrzeżenie, a nie błąd (i niezupełnie zalecane ani przenośne). Ale powodem, dla którego możesz robić te rzeczy, jest podstawowe C.
/// Represents an instance of a class.
struct objc_object {
Class isa OBJC_ISA_AVAILABILITY;
};
/// A pointer to an instance of a class.
typedef struct objc_object *id;
Powyższy kod pochodzi z objc.h, więc wygląda na to, że id jest instancją struktury objc_object, a wskaźnik isa może wiązać się z dowolnym obiektem klasy Objective C, podczas gdy void * jest po prostu wskaźnikiem bez typu.
Rozumiem, że id reprezentuje wskaźnik do obiektu, podczas gdy void * może wskazywać na wszystko, o ile następnie rzutujesz go na typ, w którym chcesz go użyć
Oprócz tego, co już zostało powiedziane, istnieje różnica między obiektami a wskaźnikami związanymi z kolekcjami. Na przykład, jeśli chcesz umieścić coś w NSArray, potrzebujesz obiektu (typu „id”) i nie możesz tam użyć wskaźnika surowych danych (typu „void *”). Możesz użyć [NSValue valueWithPointer:rawData]
do przekonwertowania void *rawDdata
na typ „id”, aby użyć go w kolekcji. Ogólnie „id” jest bardziej elastyczny i ma więcej semantyki związanej z dołączonymi do niego obiektami. Jest więcej przykładów wyjaśniających typ id Objective C tutaj .
id
zakłada się , że an odpowiada-retain
i-release
, podczas gdy avoid*
jest całkowicie nieprzezroczysty dla wywoływanego. Nie możesz przekazać dowolnego wskaźnika do-performSelector:withObject:afterDelay:
(zachowuje obiekt) i nie możesz założyć, że+[UIView beginAnimations:context:]
zachowa kontekst (delegat animacji powinien zachować własność kontekstu; UIKit zachowuje delegata animacji).