Какая разница между объявлением переменной "id" и "NSObject"?

В Objective-C, какая разница между объявлением переменной 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];