Цель-C:разница между id и void *
-
19-09-2019 - |
Вопрос
В чем разница между id
и void *
?
Решение
void *
означает "ссылка на некоторый случайный фрагмент памяти с нетипизированным / неизвестным содержимым".
id
означает "ссылка на какой-то случайный объект Objective-C неизвестного класса".
Существуют дополнительные семантические различия:
В режимах Только GC или поддерживаемых GC компилятор будет создавать барьеры записи для ссылок типа
id
, но не для типаvoid *
.При объявлении структур это может быть критическим отличием.Объявляя ивары такими , какvoid *_superPrivateDoNotTouch;
приведет к преждевременной жатве предметов, если_superPrivateDoNotTouch
на самом деле это объект.Не делай этого.попытка вызвать метод по ссылке на
void *
type выдаст предупреждение компилятора.попытка вызвать метод на
id
type будет предупреждать только в том случае, если вызываемый метод не был объявлен ни в одном из@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
аргумент метода through в качестве context
аргумент.Что касается NSArray, это непрозрачный массив данных размером с указатель, и вы можете свободно использовать его для любых целей, которые вы хотите.
Без функции типа замыкания в языке это единственный способ перенести большой объем данных с помощью функции.Пример;если бы вы хотели, чтобы mySortFunc() выполнял условную сортировку с учетом регистра или без учета регистра, оставаясь при этом потокобезопасным, вы бы передали индикатор is-case-sensitive в контексте, вероятно, применяя его при входе и выходе.
Хрупкий и подверженный ошибкам, но единственный способ.
Блоки решают эту проблему - Блоки являются замыканиями для C.Они доступны в Clang -- http://llvm.org/ и широко распространены у Снежного Барса (http://developer.apple.com/library/ios/documentation/Performance/Reference/GCD_libdispatch_Ref/GCD_libdispatch_Ref.pdf).
Другие советы
id - это указатель на объект objective C, где as void * - это указатель на что угодно.
id также отключает предупреждения, связанные с вызовом неизвестных методов, так, например:
[(id)obj doSomethingWeirdYouveNeverHeardOf];
не будет выдавать обычного предупреждения о неизвестных методах.Это, конечно, вызовет исключение во время выполнения, если только obj не равен нулю или действительно не реализует этот метод.
Часто вам следует использовать NSObject*
или id<NSObject>
в предпочтении к id
, что, по крайней мере, подтверждает, что возвращаемый объект является объектом Cocoa, поэтому вы можете безопасно использовать для него такие методы, как retain / release /autorelease.
Если метод имеет возвращаемый тип 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 и юниксджанки.blogspot.com/2008/03/id-vs-nsobject-vs-id.html.
/// 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 здесь.