Приватный ivar в @interface или @implementation

Есть ли причина объявлять приватный ивар в @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

person Yevgeniy    schedule 12.01.2012    source источник


Ответы (3)


Объявление переменных экземпляра в @implementation — это недавняя функция Obj-C, поэтому вы видите много кода с ними в @interface — другого выбора не было.

Если вы используете компилятор, который поддерживает объявление переменных экземпляра в реализации, объявляющей их, вероятно, лучшее значение по умолчанию — размещать их в интерфейсе только в том случае, если к ним нужен доступ другим.

Изменить: дополнительная информация

Переменные экземпляра, объявленные в реализации, неявно скрыты (фактически частные), а видимость нельзя изменить — @public, @protected и @private не вызывают ошибок компиляции (с текущим Clang в как минимум), но игнорируются.

person CRD    schedule 12.01.2012
comment
В частности, речь идет о компиляторе Clang › 2. (Существующий) GCC этого не сделает. - person jscs; 12.01.2012
comment
@ranReloaded - нет. Есть gcc — фронтенд и бэкенд gcc, gcc-llvm — фронтенд gcc, бэкэнд llvm — и clang — фронтенд clang, бэкэнд llvm. Я тестировал только на clang, Джош тестировал на одном из gcc. YMMV, просто попробуйте с любым компилятором, который вы используете, и посмотрите. - person CRD; 27.06.2012

Вы бы предпочли @interface, если вам нужна поддержка компилятора для более старых систем или выпусков Xcode.

Если вы уверены, что вам не понадобится эта обратная совместимость, я бы сказал, что лучше поместить ее в папку @implementation.

  • Я думаю, что @private — это хороший вариант по умолчанию.
  • Он минимизирует время компиляции и уменьшает количество зависимостей, если вы используете его правильно.
  • Вы можете уменьшить большую часть этого шума в верхней части заголовка. Многие люди помещают #imports для своих иваров, но они должны использовать предварительное объявление по умолчанию. Таким образом, вы можете удалить многие #imports и многие предварительные объявления из своего заголовка.
person justin    schedule 12.01.2012

Директивы @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: предупреждение: переменная экземпляра «y» — это @private; это будет грубая ошибка в будущем

Main.m:57:1: предупреждение: переменная экземпляра «y» — это @private; это будет грубая ошибка в будущем

один раз для каждого ненадлежащего доступа к частному члену y, но все равно компилирует его.

При запуске вы получаете

x = 10 , y = -10

Так что на самом деле от вас НЕ зависит писать код доступа таким образом, но поскольку objc является надмножеством C, синтаксис C работает просто отлично, и все классы прозрачны.

Вы можете настроить компилятор для обработки этих предупреждений как ошибок и залога, но внутренне Objective-C не настроен для такой строгости. Диспетчер динамического метода должен будет проверять область действия и разрешение для каждого вызова ( sloooooowwwww... ), поэтому помимо предупреждения во время компиляции система ожидает, что программист будет соблюдать область действия члена данных.

Есть несколько приемов для обеспечения конфиденциальности участников в 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). 

...

Компилятор будет выдавать предупреждения за такие вопиющие махинации, но будет продолжать и верить, что вы знаете, что делаете (правда?), и что у вас есть свои причины (а вы?). Кажется, много работы? Склонен к ошибкам? Ура, детка! Сначала попробуйте провести рефакторинг своего кода, прежде чем прибегать к магическим приемам C и подобным операциям с фрикадельками.

Но вот оно. Удачи.

person Chris Reid    schedule 04.06.2012
comment
Я бы держался подальше от double на iOS ;) - person Nicolas Miari; 27.06.2012