Объявление / определение местоположения переменных в ObjectiveC?
С тех пор, как я начал работать над приложениями iOS и objective C, я был действительно озадачен различными местами, где можно было бы объявлять и определять переменные. С одной стороны, у нас есть традиционный подход C, с другой-у нас есть новые директивы ObjectiveC, которые добавляют OO поверх этого. Не могли бы вы, ребята, помочь мне понять лучшую практику и ситуации, когда я хотел бы использовать эти места для своих переменных и, возможно, исправить мое нынешнее понимание?
здесь пример класса (.h и .м):
#import <Foundation/Foundation.h>
// 1) What do I declare here?
@interface SampleClass : NSObject
{
// 2) ivar declarations
// Pretty much never used?
}
// 3) class-specific method / property declarations
@end
и
#import "SampleClass.h"
// 4) what goes here?
@interface SampleClass()
// 5) private interface, can define private methods and properties here
@end
@implementation SampleClass
{
// 6) define ivars
}
// 7) define methods and synthesize properties from both public and private
// interfaces
@end
- мое понимание 1 и 4 заключается в том, что это объявления и определения на основе файлов в стиле C, которые не имеют никакого представления о концепции класса и, следовательно, должны использоваться именно так, как они будут использоваться в C. Я видел, как они использовались для реализации статических переменных на основе синглетов раньше. Есть ли другие удобные способы использования, которые мне не хватает?
- мое мнение от работы с iOS заключается в том, что ivars были полностью выведены за пределы директивы @synthesize и, таким образом, могут быть в основном проигнорированы. Это так?
- относительно 5: Почему я когда-нибудь захочу объявить методы в частных интерфейсах? Мои частные методы класса, похоже, компилируются просто отлично без объявления в интерфейсе. Это в основном для удобства чтения?
Спасибо, ребята!
4 ответа:
я могу понять вашу растерянность. Тем более, что последние обновления Xcode и новый компилятор LLVM изменили способ объявления ivars и свойств.
до "современного" Objective-C (в "старом" Obj-C 2.0) у вас не было большого выбора. Переменные экземпляра, используемые для объявления в заголовке между фигурными скобками
{ }
:// MyClass.h @interface MyClass : NSObject { int myVar; } @end
вы смогли получить доступ к этим переменным только в своей реализации, но не из других классов. Чтобы сделать это, вы пришлось объявить методы доступа, которые выглядят примерно так:
// MyClass.h @interface MyClass : NSObject { int myVar; } - (int)myVar; - (void)setMyVar:(int)newVar; @end // MyClass.m @implementation MyClass - (int)myVar { return myVar; } - (void)setMyVar:(int)newVar { if (newVar != myVar) { myVar = newVar; } } @end
таким образом, вы смогли получить и установить эту переменную экземпляра из других классов, используя обычный синтаксис квадратных скобок для отправки сообщений (методы вызова):
// OtherClass.m int v = [myClass myVar]; // assuming myClass is an object of type MyClass. [myClass setMyVar:v+1];
потому что ручное объявление и реализация каждого метода доступа было довольно раздражающим,
@property
и@synthesize
были введены для автоматического создания методов доступа:// MyClass.h @interface MyClass : NSObject { int myVar; } @property (nonatomic) int myVar; @end // MyClass.m @implementation MyClass @synthesize myVar; @end
результат намного более четкий и короткий код. Методы доступа будут реализованы для вас, и вы все равно можете использовать синтаксис скобок, как и раньше. Но кроме того, вы также можете использовать синтаксис точки для доступа к свойствам:
// OtherClass.m int v = myClass.myVar; // assuming myClass is an object of type MyClass. myClass.myVar = v+1;
С Xcode 4.4 вам больше не нужно объявлять переменную экземпляра самостоятельно, и вы можете пропустить
@synthesize
тоже. Если вы не объявите ivar, компилятор добавит его для вас, и он также будет генерировать методы доступа без необходимости использования@synthesize
.имя по умолчанию для автоматически созданного ivar - это имя или ваше свойство, начинающееся с подчеркивания. Вы можете изменить имя сгенерированного ivar с помощью
@synthesize myVar = iVarName;
// MyClass.h @interface MyClass : NSObject @property (nonatomic) int myVar; @end // MyClass.m @implementation MyClass @end
это будет работать точно так же, как код выше. По соображениям совместимости вы все еще можете объявить ivars в заголовке. Но поскольку единственная причина, по которой вы хотите это сделать (а не объявлять свойство), - это создать закрытую переменную, теперь вы можете сделать это в файл реализации, а также и это предпочтительный способ.
An
@interface
блок в файле реализации-это на самом деле расширение и может использоваться для переадресации методов объявления (больше не требуется) и для (повторного)объявления свойств. Вы могли бы, например, объявитьreadonly
свойство в заголовке.@property (nonatomic, readonly) myReadOnlyVar;
и повторно объявить его в файле реализации как
readwrite
чтобы иметь возможность установить его с помощью синтаксиса свойства, а не только через прямой доступ к Ивар.что касается объявления переменных полностью вне любого
@interface
или@implementation
блок, да это простые переменные C и работают точно так же.
во-первых, прочитайте ответ @DrummerB. Это хороший обзор того, почему и что вы должны Вообще делать. Имея это в виду, на ваши конкретные вопросы:
#import <Foundation/Foundation.h> // 1) What do I declare here?
здесь нет фактических определений переменных (это технически законно, если вы точно знаете, что делаете, но никогда не делайте этого). Вы можете определить несколько других видов вещей:
- typdefs
- метки
- экстернов
экстерны выглядят так объявления переменных, но они просто обещание на самом деле объявить его где-то еще. В ObjC они должны использоваться только для объявления констант и, как правило, только строковых констант. Например:
extern NSString * const MYSomethingHappenedNotification;
вы бы тогда в своем
.m
файл объявляет фактическую константу:NSString * const MYSomethingHappenedNotification = @"MYSomethingHappenedNotification";
@interface SampleClass : NSObject { // 2) ivar declarations // Pretty much never used? }
как отмечает DrummerB,это наследие. Ничего сюда не клади.
// 3) class-specific method / property declarations @end
да.
#import "SampleClass.h" // 4) what goes here?
внешний константы, как описано выше. Также файл статических переменных можно перейти сюда. Это эквивалент переменных класса в других языках.
@interface SampleClass() // 5) private interface, can define private methods and properties here @end
да
@implementation SampleClass { // 6) define ivars }
но очень редко. Почти всегда вы должны позволить clang (Xcode) создавать переменные для вас. Исключения обычно относятся к не ObjC ivars (например, объекты Core Foundation и особенно объекты C++, если это класс ObjC++) или ivars, которые имеют странную семантику хранения (например ивары, которые по какой-то причине не соответствуют свойству).
// 7) define methods and synthesize properties from both public and private // interfaces
как правило, вы не должны @синтезировать больше. Clang (Xcode) сделает это за вас, и вы должны позволить ему.
за последние несколько лет, все стало значительно проще. Побочным эффектом является то, что в настоящее время существует три разных эпохи (хрупкий ABI, не хрупкий ABI, не хрупкий ABI + auto-syntheisze). Поэтому, когда вы видите старый код, это может быть немного запутанным. Таким образом, путаница, возникающая из простота :Д
Я тоже довольно новый, так что надеюсь не облажаться.
1 & 4: глобальные переменные в стиле C: они имеют широкий диапазон файлов. Разница между ними заключается в том, что, поскольку они имеют ширину файла, первый будет доступен любому, кто импортирует заголовок, а второй-нет.
2: переменные экземпляра. Большинство переменных экземпляра синтезируются и извлекаются/устанавливаются через методы доступа с использованием свойств, потому что это делает управление памятью приятным и простым, а также дает вы легко понимаете точечную нотацию.
6: реализация ивары несколько новые. Это хорошее место для размещения частных Ивар, так как вы хотите только выставить то, что нужно в публичном заголовке, но подклассы не наследуют их AFAIK.
3 & 7: публичные объявления методов и свойств, а затем реализации.
5: частный интерфейс. Я всегда использую частные интерфейсы, когда могу, чтобы держать вещи в чистоте и создавать своего рода эффект черного ящика. Если им это не нужно знай об этом, положи туда. Я также делаю это для удобства чтения, не знаю, есть ли какие-либо другие причины.
это пример всех видов переменных, объявленных в Objective-C. имя переменной указывает на ее доступ.
: Животные.h: Животные.м@interface Animal : NSObject { NSObject *iProtected; @package NSObject *iPackage; @private NSObject *iPrivate; @protected NSObject *iProtected2; // default access. Only visible to subclasses. @public NSObject *iPublic; } @property (nonatomic,strong) NSObject *iPublic2; @end
#import "Animal.h" // Same behaviour for categories (x) than for class extensions (). @interface Animal(){ @public NSString *iNotVisible; } @property (nonatomic,strong) NSObject *iNotVisible2; @end @implementation Animal { @public NSString *iNotVisible3; } -(id) init { self = [super init]; if (self){ iProtected = @"iProtected"; iPackage = @"iPackage"; iPrivate = @"iPrivate"; iProtected2 = @"iProtected2"; iPublic = @"iPublic"; _iPublic2 = @"iPublic2"; iNotVisible = @"iNotVisible"; _iNotVisible2 = @"iNotVisible2"; iNotVisible3 = @"iNotVisible3"; } return self; } @end
обратите внимание, что переменные iNotVisible не видны из любого другого класса. Это проблема видимости, поэтому объявление их с
@property
или@public
ничего не изменится.внутри конструктора рекомендуется обращаться к переменным, объявленным с помощью
@property
использование подчеркивания вместоself
чтобы избежать побочных эффектов.давайте попробуем получить доступ к переменные.
: Корова.h: Корова.м#import "Animal.h" @interface Cow : Animal @end
#import "Cow.h" #include <objc/runtime.h> @implementation Cow -(id)init { self=[super init]; if (self){ iProtected = @"iProtected"; iPackage = @"iPackage"; //iPrivate = @"iPrivate"; // compiler error: variable is private iProtected2 = @"iProtected2"; iPublic = @"iPublic"; self.iPublic2 = @"iPublic2"; // using self because the backing ivar is private //iNotVisible = @"iNotVisible"; // compiler error: undeclared identifier //_iNotVisible2 = @"iNotVisible2"; // compiler error: undeclared identifier //iNotVisible3 = @"iNotVisible3"; // compiler error: undeclared identifier } return self; } @end
мы все еще можем получить доступ к невидимым переменным с помощью среды выполнения.
: Корова.м (часть 2)@implementation Cow(blindAcess) - (void) setIvar:(NSString*)name value:(id)value { Ivar ivar = class_getInstanceVariable([self class], [name UTF8String]); object_setIvar(self, ivar, value); } - (id) getIvar:(NSString*)name { Ivar ivar = class_getInstanceVariable([self class], [name UTF8String]); id thing = object_getIvar(self, ivar); return thing; } -(void) blindAccess { [self setIvar:@"iNotVisible" value:@"iMadeVisible"]; [self setIvar:@"_iNotVisible2" value:@"iMadeVisible2"]; [self setIvar:@"iNotVisible3" value:@"iMadeVisible3"]; NSLog(@"\n%@ \n%@ \n%@", [self getIvar:@"iNotVisible"], [self getIvar:@"_iNotVisible2"], [self getIvar:@"iNotVisible3"]); } @end
давайте попробуем получить доступ к невидимым переменным.
File: main.м
#import "Cow.h" #import <Foundation/Foundation.h> int main(int argc, char *argv[]) { @autoreleasepool { Cow *cow = [Cow new]; [cow performSelector:@selector(blindAccess)]; } }
этот отпечатки
iMadeVisible iMadeVisible2 iMadeVisible3
обратите внимание, что я смог получить доступ к поддержке ivar
_iNotVisible2
который является частным для подкласса. В Objective-C все переменные могут быть прочитаны или изменены, даже те, которые помечены@private
, без исключений.я не включил связанные объекты или переменные C, поскольку они разные птицы. Что касается переменных C, то любая переменная, определенная вне
@interface X{}
или@implementation X{}
- это переменная C с областью действия файла и статическим хранилищем.я не обсуждал памяти атрибуты управления, или только для чтения/readwrite, getter/setter атрибуты.