Подтвердить что ты не робот

Частный 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
4b9b3361

Ответ 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! Попробуйте переформулировать свой код сначала, прежде чем прибегать к магическим трюкам и футбольным операциям.

Но это так. Удачи.