Частный ivar в @interface или @implementation
Есть ли какая-нибудь причина для объявления личного ivar в @interface
вместо @implementation
?
Я вижу такой код по всему Интернету (включая документацию, представленную Apple):
foo.h
@interface Foo : NSObject {
@private
id _foo;
}
@end
Foo.m
@implementation Foo
// do something with _foo
@end
Заголовочный файл определяет открытый интерфейс класса, тогда как частный ivar... хорошо... частный. Так почему бы не объявить это так?
foo.h
@interface Foo : NSObject
@end
Foo.m
@implementation Foo {
@private
id _foo;
}
// do something with _foo
@end
Ответы
Ответ 1
Объявление переменных экземпляра в @implementation
является недавней особенностью Obj-C, поэтому вы видите много кода с ними в @interface
- другого выбора не было.
Если вы используете компилятор, который поддерживает объявление переменных экземпляра в реализации, объявляющих их, вероятно, лучший по умолчанию - только помещайте их в интерфейс, если к ним нужно обращаться другими.
Изменить: Дополнительная информация
Переменные экземпляра, объявленные в реализации, неявно скрыты (фактически закрыты), а видимость не может быть изменена - @public
, @protected
и @private
не создают ошибки компилятора (с текущим Clang как минимум), но игнорируются.
Ответ 2
Вы бы одобрили @interface
, если вам нужна поддержка компилятора, ориентированная на более старые системы или выпуски Xcode.
Если вы уверены, что вам не понадобится эта обратная совместимость, я бы сказал, что лучше разместить ее в @implementation
.
- Я думаю, что @private является хорошим дефолтом.
- Он минимизирует время компиляции и уменьшает зависимости, если вы используете его правильно.
- Вы можете уменьшить большую часть этого шума в верхней части заголовка. Многие люди будут помещать #imports для своих иваров, но они должны использовать форвардное объявление по умолчанию. Таким образом, вы можете удалить много заголовков #imports и много объявлений из своего заголовка.
Ответ 3
Директивы @public, @protected и @private
не привязаны к objective-C, они являются подсказками компилятора о
доступность переменных.
Это НЕ ОГРАНИЧИВАЕТ ВАС от доступа к ним.
пример:
@interface Example : Object
{
@public
int x;
@private
int y;
}
...
...
id ex = [[Example alloc ] init];
ex->x = 10;
ex->y = -10;
printf(" x = %d , y = %d \n", ex->x , ex->y );
...
Компилятор gcc выдает:
Main.m: 56: 1: warning: переменная экземпляра 'y is @private; это будет трудной ошибкой в будущем
Main.m: 57: 1: warning: переменная экземпляра 'y is @private; это будет трудной ошибкой в будущем
один раз для каждого "непринужденного" доступа к элементу "private" y, но компилирует его в любом случае.
При запуске вы получаете
x = 10 , y = -10
Так что вам действительно не нужно писать код доступа таким образом, но поскольку objc является надмножеством C,
Синтаксис C работает просто отлично, и все классы прозрачны.
Вы можете настроить компилятор для обработки этих предупреждений как ошибок и залогов, но objective-C не настроен внутренне для такой строгости. Для диспетчеризации динамических методов необходимо будет проверить объем и разрешение для каждого вызова (slooooowwwww...), поэтому за пределами предупреждения о компиляции система ожидает, что программист будет оценивать область данных.
Есть несколько трюков для получения конфиденциальности членов в objective-C.
Один из них - убедиться, что вы поместили интерфейс и реализации вашего класса в отдельные файлы .h и .m, соответственно, и поместили члены данных в файл реализации (файл .m).
Тогда файлы, которые импортируют заголовки, не имеют доступа к членам данных, а только сам класс.
Затем укажите методы доступа (или нет) в заголовке. Вы можете реализовать функции setter/getter
в файле реализации для диагностических целей, если вы хотите, и они будут вызываемыми,
но прямого доступа к данным не будет.
Пример:
@implementation Example2 :Object
{
//nothing here
}
double hidden_d; // hey now this isn't seen by other files.
id classdata; // neither is this.
-(id) classdata { return [classdata data]; } // public accessor
-(void) method2 { ... }
@end
// this is an "informal category" with no @interface section
// these methods are not "published" in the header but are valid for the class
@implementation Example2 (private)
-(void)set_hidden_d:(double)d { hidden_d = d; }
// You can only return by reference, not value, and the runtime sees (id) outside this file.
// You must cast to (double*) and de-reference it to use it outside of this file.
-(id) hidden_d_ptr { return &hidden_d;}
@end
...
[Main.m]
...
ex2 = [[Example2 alloc] init];
double d = ex2->hidden_d; // error: 'struct Example2’ has no member named ‘hidden_d’
id data = ex2->classdata; // error: 'struct Example2’ has no member named ‘classdata’
id data = [ex2 classdata] // OK
[ex2 set_hidden_d : 6.28318 ]; // warning:'Example2' may not respond to '-set_hidden_d:'
double* dp = [ex2 hidden_d_ptr]; // (SO UGLY) warning: initialization from incompatible pointer type
// use (double*)cast -- <pointer-to-pointer conversion>
double d = (*dp); // dereference pointer (also UGLY).
...
Компилятор выдаст предупреждения для таких вопиющих махинаций, но продолжит
и верите, что вы знаете, что делаете (действительно?), и что у вас есть свои причины (не так ли?).
Кажется, много работы? Ошибка? Yay Baby!
Попробуйте переформулировать свой код сначала, прежде чем прибегать к магическим трюкам и футбольным операциям.
Но это так. Удачи.