Відповіді:
void *
означає "посилання на деякий випадковий фрагмент пам'яті з нетиповим / невідомим вмістом"
id
означає "посилання на якийсь випадковий об'єкт Objective-C невідомого класу"
Є додаткові смислові відмінності:
У режимах GC Only або GC, що підтримуються, компілятор видасть бар'єри запису для посилань типу id
, але не для типу void *
. При декларуванні структур це може бути критичною різницею. Оголошення iVars як подібне void *_superPrivateDoNotTouch;
призведе до передчасного пожвавлення об'єктів, якщо _superPrivateDoNotTouch
він є об'єктом. Не робіть цього.
спроба виклику методу на посилання void *
типу видалить попередження компілятора.
спроба виклику методу на id
тип попередить лише тоді, коли викликаний метод не був оголошений ні в одній з @interface
декларацій, які бачив компілятор.
Таким чином, ніколи не слід називати об’єкт як a 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>
класи, що не реалізуються, з API API та отримати дуже далеко, це точно!
id
і Class
типи трактуються як вказівний об'єкт, що можна отримати в ARC. Тож припущення вірно принаймні за ARC.
id - вказівник на об'єктивний об'єкт C, де as void * - вказівник на що-небудь.
id також вимикає попередження, пов’язані з викликом невідомих mthods, наприклад:
[(id)obj doSomethingWeirdYouveNeverHeardOf];
не дасть звичайного попередження про невідомі методи. Звичайно, це призведе до виключення під час виконання, якщо obj не дорівнює чи дійсно не реалізує цей метод.
Часто вам слід використовувати NSObject*
або id<NSObject>
віддавати перевагу тому id
, що хоча б підтверджує, що повернутий об’єкт є об'єктом какао, тому ви можете сміливо використовувати такі методи, як утримувати / випускати / автовипускати на ньому.
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
вважається, що відповідь на,-retain
а-release
, а,void*
є абсолютно непрозорою для кальє. Ви не можете передати довільний покажчик на-performSelector:withObject:afterDelay:
(він зберігає об'єкт), і ви не можете припустити, що+[UIView beginAnimations:context:]
він збереже контекст (делегат анімації повинен зберігати право власності на контекст; UIKit зберігає делегата анімації).