2009-08-20 13 views

Odpowiedz

231

void * znaczy „w odniesieniu do pewnych losowej fragmentu o” pamięci z niewstukane/nieznanych zawartości”

id oznacza«w odniesieniu do pewnych losowej Cel C obiektu o nieznanym klasy»

Istnieje dodatkowy semantycznej różnice:

  • Pod GC lub GC Tylko Obsługiwane tryby, kompilator będzie emitować barier zapisu o referencje typu id, ale nie na typ void *. Deklaracja struktur może być krytyczną różnicą. Deklarowanie zmiennych typu iVar, takich jak void *_superPrivateDoNotTouch;, spowoduje przedwczesne pobieranie obiektów, jeśli obiekt jest faktycznie obiektem _superPrivateDoNotTouch. Nie rób tego.

  • próba wywołania metody na podstawie numeru void * spowoduje wyświetlenie ostrzeżenia kompilatora.

  • próbuje wywołać metodę na typ id ostrzega tylko, jeśli metoda jest nazywana nie została zadeklarowana w żadnej deklaracji @interface widziana przez kompilator.

Zatem nigdy nie powinny odnosić się do obiektu jako void *. Podobnie, należy unikać używania zmiennej pisanej id w odniesieniu do obiektu. Użyj najbardziej charakterystycznego odniesienia dla klasy, które możesz wpisać. Nawet NSObject * jest lepszy niż id, ponieważ kompilator może przynajmniej zapewnić lepszą walidację wywołań metod względem tego odwołania.

Jedynym powszechnym i prawidłowym użyciem void * jest nieprzezroczyste odniesienie do danych przekazywane za pośrednictwem innego interfejsu API.

rozważyć metodę sortedArrayUsingFunction: context: z 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 jedynie przechodzi cokolwiek przekazać w jak context argument metoda za pomocą argumentu context. Jest to nieprzejrzysty fragment danych o wskaźnikach, o ile chodzi o NSArray, i możesz go używać do dowolnego celu.

Bez funkcji zamykania w języku, jest to jedyny sposób przenoszenia całej porcji danych za pomocą funkcji. Przykład; jeśli chcesz, aby funkcja mySortFunc() była warunkowo sortowana jako wielkość liter lub wielkość liter, a jednocześnie nadal jest bezpieczna dla wątków, możesz przekazać wskaźnik uwzględniający wielkość liter w kontekście, najprawdopodobniej rzutując w drodze i wychodząc.

Kruchy i podatny na błędy, ale jedyny sposób.

Bloki rozwiązują to - bloki są zamknięciami dla C. Są one dostępne w Clang - http://llvm.org/ i są wszechobecne w systemie Snow Leopard (http://developer.apple.com/library/ios/documentation/Performance/Reference/GCD_libdispatch_Ref/GCD_libdispatch_Ref.pdf).

+0

Dodatkowo, jestem prawie pewien, że "id" ma odpowiadać na '-retain' i' -release', podczas gdy 'void *' jest całkowicie nieprzejrzysty dla uczestnika. Nie możesz przekazać arbitralnego 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). –

+3

Nie można przyjąć żadnych założeń dotyczących odpowiedzi na pytanie "id". "Identyfikator" może z łatwością odnosić się do instancji klasy, która nie ma charakteru 'NSObject'. Praktycznie rzecz biorąc, twoje zdanie najlepiej pasuje do zachowań w świecie rzeczywistym; nie możesz mieszać innych niż '' klas implementujących z Foundation API i uzyskać bardzo daleko, to na pewno na pewno! – bbum

+2

Teraz typy "id" i "Class" są traktowane jako [* możliwy do zatrzymania wskaźnik obiektu *] (http://clang.llvm.org/docs/AutomaticReferenceCounting.html#objects) w ramach ARC. Tak więc założenie jest prawdziwe przynajmniej w ARC. – Eonil

8

Jeśli metoda ma typ zwrotu id, możesz zwrócić dowolny obiekt Objective-C.

void oznacza, że ​​metoda niczego nie zwróci.

void * to tylko wskaźnik. Nie będzie można edytować treści na adres wskazany przez wskaźnik.

+2

W odniesieniu do wartości zwracanej metody, przeważnie w prawo. Dotyczy to deklarowania zmiennych lub argumentów, nie do końca. I zawsze możesz rzucić (void *) na bardziej konkretny typ, jeśli chcesz odczytać/zapisać zawartość - nie jest to dobry pomysł. – bbum

2

moim rozumieniu jest to, że id reprezentuje wskaźnik do obiektu podczas void * może wskazywać na coś naprawdę, jak długo potem wrzucił je do rodzaju, którego chcesz użyć go jako

+0

Jeśli przenosisz z (void *) do jakiegoś typu obiektu, w tym id, najprawdopodobniej zrobisz to źle. Istnieją ku temu powody, ale są one nieliczne, dalekie i prawie zawsze wskazują na wadę projektu. – bbum

+1

Cytat "istnieją powody, aby to zrobić, ale są one nieliczne, daleko pomiędzy" prawda, że. To zależy od sytuacji. Jednak nie przytoczę krótkiego stwierdzenia, jak "bardzo prawdopodobne, że to źle" bez określonego kontekstu. – hhafez

+0

Zrobiłbym ogólne oświadczenie; musiał polować i naprawiać zbyt wiele przeklętych błędów z powodu rzucania do niewłaściwego typu z pustką * pomiędzy.Jedynym wyjątkiem są interfejsy API oparte na oddzwanianiu, które pobierają nieważny * kontekstowy argument, którego umowa stwierdza, że ​​kontekst pozostanie niezmieniony między skonfigurowaniem wywołania zwrotnego a odebraniem wywołania zwrotnego. – bbum

19

id jest wskaźnikiem do obiekt obiektu C, gdzie jako void * jest wskaźnikiem do czegokolwiek.

id wyłącza również ostrzeżenia związane z wywołaniem nieznanych mthods, więc na przykład:

[(id)obj doSomethingWeirdYouveNeverHeardOf];

nie da zwykłe ostrzeżenie o nieznanych metod. Będzie oczywiście podnosić wyjątek w czasie wykonywania, chyba że obiekt jest zerowy lub naprawdę zaimplementuje tę metodę.

Często należy użyć NSObject* lub id<NSObject> zamiast do id, który przynajmniej potwierdza, że ​​obiekt zwrócony jest obiektem Cocoa, dzięki czemu można bezpiecznie korzystać z metod takich jak zachowują/zwolnienia/autorelease na nim.

+2

W przypadku wywołań metod cel typu (id) wygeneruje ostrzeżenie, jeśli docelowa metoda nie została nigdzie zadeklarowana. Tak więc, w twoim przykładzie, musi zostać gdzieś zadeklarowane, aby nie było ostrzeżenia. – bbum

+0

Masz rację, lepszy przykład może być coś takiego jak storagePolicy. –

+0

@PeterNLewis Nie zgadzam się na 'Często powinieneś używać NSObject *' zamiast 'id'. Podając 'NSObject *', wyraźnie mówisz, że obiekt jest obiektem NSObject. Każde wywołanie metody do obiektu spowoduje ostrzeżenie, ale bez wyjątku środowiska wykonawczego tak długo, jak długo ten obiekt rzeczywiście odpowiada wywołaniu metody. Ostrzeżenie jest oczywiście denerwujące, więc "id" jest lepsze. Gruboziarnisty może być bardziej szczegółowy, na przykład mówiąc "id ", co w tym przypadku oznacza cokolwiek obiektu, musi być zgodny z protokołem MKAnnotation. – pnizzle

7

id to wskaźnik do obiektu Objective-C. void * to wskaźnik do dowolnie. Można użyć void * zamiast id, ale nie jest to zalecane, ponieważ nigdy nie otrzymasz ostrzeżeń kompilatora.

Możesz chcieć 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.

+1

Niezupełnie. (void *) zmienne wpisane nie mogą w ogóle być celem wywołań metod. Powoduje to "ostrzeżenie: nieprawidłowy typ odbiornika" void * "" z kompilatora. – bbum

+0

@bbum: 'void *' zmienne wpisane zdecydowanie mogą być celem wywołań metod - to ostrzeżenie, a nie błąd. Co więcej, możesz to zrobić: 'int i = (int) @" Hello, string! ";' I wykonaj następujące czynności: 'printf (" Wysyłanie do int: '% s' \ n ", [i UTF8String ]); '. Jest to ostrzeżenie, a nie błąd (i nie jest dokładnie zalecane, ani przenośne). Ale powód, dla którego możesz zrobić te rzeczy, jest wszystkim podstawowym C. – johne

+0

Przepraszamy. Masz rację; to ostrzeżenie, a nie błąd. Ostrzeżenia traktuję jak błędy zawsze i wszędzie. – bbum

4
/// 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; 

ten kod jest z objc.h tak wygląda id wystąpienie objc_object struct i ISA wskaźnik może wiązać się z żadną Cel przedmiotu klasy C, natomiast próżnia * jest tylko typu wskaźnikiem.

0

Oprócz tego, co już zostało powiedziane, istnieje różnica między obiektami i 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 danych surowych (typu "void *"). Możesz użyć [NSValue valueWithPointer:rawData], aby przekonwertować void *rawDdata na typ "id", aby użyć go wewnątrz kolekcji. Ogólnie "id" jest bardziej elastyczny i ma więcej semantyki związanej z obiektami do niego dołączonymi. Istnieje więcej przykładów wyjaśniających id type of Objective C here.