Ответы:
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 ).
id
отвечает. id
Может легко обратиться к экземпляру класса , который не присущ от NSObject
. Тем не менее, на практике ваше утверждение лучше всего соответствует поведению в реальном мире; Вы не можете смешивать не <NSObject>
реализующие классы с Foundation API и далеко продвинуться, это точно!
id
и Class
типы обрабатываются как указатель сохраняемого объекта в ARC. Таким образом, предположение верно, по крайней мере, при ARC.
id - это указатель на целевой объект C, где void * - указатель на что-либо.
id также отключает предупреждения, связанные с вызовом неизвестных методов, например:
[(id)obj doSomethingWeirdYouveNeverHeardOf];
не даст обычного предупреждения о неизвестных методах. Это, конечно, вызовет исключение во время выполнения, если obj не равен nil или действительно не реализует этот метод.
Часто вы должны использовать NSObject*
или id<NSObject>
в предпочтении id
, что по крайней мере подтверждает, что возвращаемый объект является объектом Какао, поэтому вы можете безопасно использовать такие методы, как retain / release / autorelease для него.
Often you should use NSObject*
а не id
. Указывая, NSObject*
вы на самом деле явно говорите, что объект является NSObject. Любой вызов метода для объекта вызовет предупреждение, но не исключение времени выполнения, если этот объект действительно отвечает на вызов метода. Предупреждение явно раздражает, тем id
лучше. Конечно, вы можете быть более точными, например, говоря id<MKAnnotation>
, что в данном случае означает, что объект должен соответствовать протоколу MKAnnotation.
Если у метода есть тип возврата, id
вы можете вернуть любой объект Objective-C.
void
значит, метод ничего не вернет.
void *
это просто указатель. Вы не сможете редактировать содержимое по адресу, на который указывает указатель.
id
указатель на объект Objective-C void *
это указатель на что-нибудь . Вы можете использовать void *
вместо id
, но это не рекомендуется, потому что вы никогда не получите предупреждения компилятора ни за что.
Возможно, вы захотите увидеть stackoverflow.com/questions/466777/whats-the-difference-between-declaring-a-variable-id-and-nsobject и unixjunkie.blogspot.com/2008/03/id-vs-nsobject-vs -id.html .
void *
типизированные переменные определенно могут быть целью вызовов методов - это предупреждение, а не ошибка. Мало того, что вы можете сделать это: int i = (int)@"Hello, string!";
и следовать с: printf("Sending to an int: '%s'\n", [i UTF8String]);
. Это предупреждение, а не ошибка (и не совсем рекомендуемое, ни переносимое). Но причина, по которой вы можете делать эти вещи, заключается в
/// 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;
Вышеприведенный код взят из objc.h, поэтому похоже, что id является экземпляром структуры objc_object, и указатель isa может связываться с любым объектом класса Objective C, а void * - просто нетипизированный указатель.
Насколько я понимаю, id представляет собой указатель на объект, в то время как void * может указывать на что-либо действительно, при условии, что вы приведете его к типу, который вы хотите использовать как
В дополнение к тому, что уже сказано, есть различие между объектами и указателями, связанными с коллекциями. Например, если вы хотите поместить что-то в NSArray, вам нужен объект (типа "id"), и вы не можете использовать там указатель необработанных данных (типа "void *"). Вы можете использовать [NSValue valueWithPointer:rawData]
для преобразования void *rawDdata
в тип "id" для использования его внутри коллекции. В общем, «id» более гибок и имеет больше семантики, связанной с объектами, прикрепленными к нему. Есть больше примеров, объясняющих тип идентификатора Objective C здесь .
id
предполагается , что an отвечает на-retain
и-release
, тогда как avoid*
совершенно непрозрачен для вызываемого. Вы не можете передать произвольный указатель на-performSelector:withObject:afterDelay:
(он сохраняет объект), и вы не можете предполагать, что+[UIView beginAnimations:context:]
он сохранит контекст (делегат анимации должен сохранять владение контекстом; UIKit сохраняет делегат анимации).