void *
означает «ссылку на некоторый случайный фрагмент памяти с нетипизированным / неизвестным содержимым»
id
означает «ссылку на некоторый случайный объект Objective C неизвестного класса»
Существуют дополнительные смысловые различия:
В режимах GC Only или GC Supported компилятор будет создавать барьеры записи для ссылок типа id
, но не для типа void *
. При объявлении структур это может быть критическим различием. Объявление iVars как void *_superPrivateDoNotTouch;
приведет к преждевременному пожатию объектов, если _superPrivateDoNotTouch
на самом деле является объектом. Не делай этого.
попытка вызвать метод по ссылке типа void *
вызовет предупреждение компилятора.
попытка вызвать метод типа id
будет предупреждать, только если вызываемый метод не был объявлен ни в одном из объявлений @interface
, которые видит компилятор.
Таким образом, никогда не следует называть объект void *
. Точно так же следует избегать использования типизированной переменной id
для ссылки на объект. Используйте наиболее конкретную типизированную ссылку класса, которую вы можете. Даже NSObject *
лучше, чем id
, потому что компилятор может, по крайней мере, обеспечить лучшую проверку вызовов методов для этой ссылки.
Единственное распространенное и правильное использование void *
- это непрозрачная ссылка на данные, передаваемая через некоторый другой API.
Рассмотрим sortedArrayUsingFunction: context:
метод NSArray
:
- (NSArray *)sortedArrayUsingFunction:(NSInteger (*)(id, id, void *))comparator context:(void *)context;
Функция сортировки будет объявлена как:
NSInteger mySortFunc(id left, id right, void *context) { ...; }
В этом случае NSArray просто передает то, что вы передаете в качестве аргумента context
методу, через аргумент context
. Что касается NSArray, то это непрозрачный кусок данных размером с указатель, и вы можете использовать его для любых целей.
Без функции типа замыкания в языке это единственный способ перенести часть данных с функцией. Пример; если вы хотите, чтобы mySortFunc () выполняла условную сортировку как чувствительную к регистру или нечувствительную к регистру, так и в то же время сохраняющую потокобезопасность, вы бы передавали индикатор с учетом регистра в контексте, что, вероятно, приводит к входу и выходу. *
Хрупкие и подверженные ошибкам, но единственный способ.
Блоки решают это - блоки являются замыканиями для C. Они доступны в Clang - http://llvm.org/ и распространены в Snow Leopard (http://developer.apple.com/library/ios/documentation/Performance/Reference/GCD_libdispatch_Ref/GCD_libdispatch_Ref.pdf).