В Objective-C, какая разница между объявлением переменной id
и объявлением ее NSObject *
?
Какая разница между объявлением переменной "id" и "NSObject"?
Ответ 1
С переменной, напечатанной id
, вы можете отправить ей любое известное сообщение, и компилятор не будет жаловаться. При переменной переменной NSObject *
вы можете отправлять сообщения, объявленные NSObject (не методы какого-либо подкласса), иначе они будут генерировать предупреждение. В общем, id
- это то, что вы хотите.
Дальнейшее объяснение: все объекты по существу имеют тип id
. Точка объявления статического типа - сообщить компилятору "Предположим, что этот объект является членом этого класса". Поэтому, если вы отправите ему сообщение, которое класс не объявляет, компилятор может сказать вам: "Подождите, этот объект не должен получать это сообщение!" Кроме того, если у двух классов есть методы с тем же именем, но с разными сигнатурами (то есть с аргументами или типами возврата), он может угадать, какой метод вы подразумеваете под классом, который вы объявили для переменной. Если он объявлен как id
, компилятор просто поднимет руки и скажет вам: "О'кей, у меня недостаточно информации здесь. Я выбираю сигнатуру метода наугад". (Обычно это не поможет, объявив NSObject*
. Обычно конфликт между двумя более конкретными классами.)
Ответ 2
id
означает "объект", NSObject *
означает "экземпляр NSObject
или один из его подклассов". Есть объекты в Objective-C, которые не являются NSObject
(те, с которыми вы встретитесь в Cocoa на данный момент: NSProxy
, Protocol
и Class
). Если какой-либо код ожидает объект определенного класса, объявление, которое помогает компилятору проверить, что вы используете его правильно. Если вы действительно можете взять "любой объект" - например, вы объявляете делегата и будете тестировать все отправки методов с помощью вызовов respondsToSelector:
- вы можете использовать id
.
Другой способ объявить объектную переменную похож на "id <NSObject>
", что означает "любой объект, реализующий протокол NSObject
.
Ответ 3
Из моего ограниченного понимания Objective-C не все объекты производятся из NSObject (в отличие от Java, где все объекты происходят от Object). Теоретически у вас могут быть другие корневые объекты. id может применяться к любому из объектов, не относящихся к NSObject.
Ответ 4
Я хотел бы добавить еще одно отличие. Когда вы добавляете протокол в id
, он больше не означает, что он будет иметь тип NSObject *
, это просто означает, что это будет любой класс, который подтверждает этот протокол.
Так, например, этот код не будет вызывать никаких ошибок, так как NSObject
category NSDelayedPerforming
имеет этот метод:
id testId;
[testId performSelector:@selector(isKindOfClass:) withObject:[NSObject class] afterDelay:.5];
Однако этот код покажет ошибку No known instance method for selector "performSelector:withObject:afterDelay:"
:
id<NSMutableCopying> testId;
[testId performSelector:@selector(isKindOfClass:) withObject:[NSObject class] afterDelay:.5];