В чем разница между этим:
ID:
#import <objc/Object.h>
@interface Forwarder : Object
{
id something;
}
NSObject:
#import <objc/Object.h>
@interface Forwarder : Object
{
NSObject *something;
}
Thz u.
В чем разница между этим:
ID:
#import <objc/Object.h>
@interface Forwarder : Object
{
id something;
}
NSObject:
#import <objc/Object.h>
@interface Forwarder : Object
{
NSObject *something;
}
Thz u.
Это сообщение в блоге Greg MILLER из unixjunkie blog суммирует различия
Некоторые выдержки:
Часто возникает путаница в различии между следующими тремя объявлениями в Objective-C:
id foo1;
NSObject *foo2;
id<NSObject> foo3;
Первый из них наиболее распространен.
Он просто объявляет указатель на некоторый объект Objective-C (см./usr/include/objc/objc.h
). id не дает компилятору никакой информации о фактическом типе объекта, поэтому компилятор не может выполнить проверку типа компиляции для вас.Просто потому, что мы знаем, что id является объектом Objective-C, не означает, что он указывает на объект, который происходит от
NSObject
, или что он даже имеет общие методы, такие как сохранение и выпуск.
Одно из решений состоит в том, чтобы статически вводить нашу переменную, используяNSObject*
, как показано выше в номере 2.
Это дает компилятору информацию о классе объекта, на который указывает foo2, поэтому компилятор может предупредить, если вы отправляете сообщение foo2, на котороеNSObject
не отвечает. Это означает, что вы можете безопасно вызывать сохранение, выпуск, описание и т.д., Но компилятор будет предупреждать, если вы вызываете длину или количество или что-либо, что не отвечаетNSObject
.Объявление объекта как
id<NSObject>
сообщает компилятору, что вам все равно, какой тип объекта является объектом, но вам все равно, что он соответствует указанному протоколуNSObject
**
.**
протокол (@protocol
) с именемNSObject
. Существует также класс с именемNSObject
, который действительно соответствует протоколуNSObject
, но это две разные вещи
Компилятор гарантирует, что все объекты, которые вы назначили этому указателю, соответствуют требуемому протоколу.
Указанный таким образом указатель может безопасно удерживать любойNSObject
(посколькуNSObject
соответствует протоколуNSObject
), но он также может содержать любойNSProxy
, потому чтоNSProxy
также соответствует протоколу NSObject.
На английском языке декларацияid<NSObject>
foo3; говорит: "foo3 - это указатель на объект любого типа, который ведет себя как NSObject".
Это очень мощный, удобный и выразительный. На самом деле, нам часто все равно, какой тип объекта, мы просто заботимся о том, чтобы он отвечал на сообщения, которые мы хотим отправить (например, сохранить, освободить).
Если вы не хотите (или не можете) проверять тип, используйте простой идентификатор. Это очень часто встречается для типов возвращаемых значений для методов, которые не знают тип возвращаемого объекта (например, + alloc). Также принято объявлять делегаты как идентификатор типа, поскольку делегаты обычно проверяются во время выполнения с помощью responseSoSelector:, и они обычно не сохраняются.
Однако, если вам нужна проверка типа компиляции, вы должны решить между вторым и третьим случаями. Ну, позвольте мне просто помочь вам - вы хотите третий случай!:-) Я очень, очень, очень редко видел ситуацию, когда NSObject * работал, но id бы не стал. И использование формы протокола имеет то преимущество, что оно будет работать с NSProxys.
Практическое различие заключается в том, что вам не нужно указывать тип id
, но обычно вам нужно придать NSObject *
для чего-то перед использованием. NSObject
- это базовый класс, в котором почти все другие классы производны от того, где id
больше относится к языковому ключевому слову.
Идентификатор реагирует на любой метод без предупреждения компилятора; NSObjects реагируют без предупреждения на методы, определенные в NSObject, включая протокол NSObject.