Переменные уровня класса Objective-C


143

У меня есть класс Film, в каждом из которых хранится уникальный идентификатор. В C #, Java и т. Д. Я могу определить статический int currentID, и каждый раз, когда я устанавливаю ID, я могу увеличивать currentID, и изменение происходит на уровне класса, а не на уровне объекта. Можно ли это сделать в Objective-C? Мне было очень трудно найти ответ на этот вопрос.

Ответы:


158

Описание проблемы :

  1. Вы хотите, чтобы у вашего ClassA была переменная класса ClassB.
  2. Вы используете Objective-C в качестве языка программирования.
  3. Objective-C не поддерживает переменные класса, как C ++.

Одна альтернатива :

Имитация поведения переменной класса с использованием возможностей Objective C

  1. Объявите / определите статическую переменную внутри classA.m, чтобы она была доступна только для методов classA (и всего, что вы помещаете в classA.m).

  2. Перезапишите метод класса инициализации NSObject, чтобы только один раз инициализировать статическую переменную экземпляром ClassB.

  3. Вам будет интересно, почему я должен перезаписывать метод инициализации NSObject. В документации Apple об этом методе есть ответ: «Среда выполнения отправляет инициализацию каждому классу в программе ровно один раз непосредственно перед тем, как классу или любому классу, который унаследован от него, отправлено его первое сообщение изнутри программы. (Таким образом, метод никогда не может быть вызван, если класс не используется.) ".

  4. Не стесняйтесь использовать статическую переменную в любом методе класса / экземпляра ClassA.

Пример кода :

файл: classA.m

static ClassB *classVariableName = nil;

@implementation ClassA

...
 
+(void) initialize
{
    if (! classVariableName)
        classVariableName = [[ClassB alloc] init];
}

+(void) classMethodName
{
    [classVariableName doSomething]; 
}

-(void) instanceMethodName
{
    [classVariableName doSomething]; 
}

...

@end

Рекомендации :

  1. Переменные класса объясняются сравнением подходов Objective-C и C ++

3
Можете ли вы иметь статическую переменную типа ClassA внутри classA.m?
goatlinks

6
это может быть глупый вопрос, но как насчет освобождения памяти? не имеет значения, потому что он должен жить так долго, пока приложение работает?
Самик

1
@samiq, посмотрите Objective-C: зачем сохранять статическую переменную? , Указатель на объект не может быть удален, но сам объект может быть удален. Вы, вероятно, не хотите выпускать его, потому что вы, скорее всего, хотите, чтобы он был доступен до тех пор, пока приложение работает, но вы сэкономите память, если вы его отпустите, поэтому, если вы знаете, что он вам больше не нужен, тогда вам следует отпустите это.
ma11hew28

5
Если initialize () гарантированно вызывается только один раз, зачем вам нужно условное выражение «if (! ClassVariableName)»?
JB

23
@jamie, initializeвызывается один раз для каждого класса (суперклассы перед подклассами), но если подкласс не переопределяет initialize, родительский класс initializeбудет вызван снова. Следовательно, требуется защита, если вы не хотите, чтобы этот код выполнялся дважды. См. Инициализация объекта класса в документации Apple по Objective-C.
big_m

31

Начиная с Xcode 8, вы можете определить свойства класса в Obj-C. Это было добавлено для взаимодействия со статическими свойствами Swift.

Objective-C теперь поддерживает свойства класса, которые взаимодействуют со свойствами типа Swift. Они объявлены как: @property (class) NSString * someStringProperty ;. Они никогда не синтезируются. (23891898)

Вот пример

@interface YourClass : NSObject

@property (class, nonatomic, assign) NSInteger currentId;

@end

@implementation YourClass

static NSInteger _currentId = 0;

+ (NSInteger)currentId {
    return _currentId;
}

+ (void)setCurrentId:(NSInteger)newValue {
    _currentId = newValue;
}

@end

Тогда вы можете получить к нему доступ так:

YourClass.currentId = 1;
val = YourClass.currentId;

Вот очень интересный пояснительный пост, который я использовал как ссылку для редактирования этого старого ответа.


2011 Ответ: (не используйте это, это ужасно)

Если вы действительно не хотите объявлять глобальную переменную, есть другая опция, может быть, не очень ортодоксальная :-), но она работает ... Вы можете объявить метод get & set следующим образом, со статической переменной внутри:

+ (NSString*)testHolder:(NSString*)_test {
    static NSString *test;

    if(_test != nil) {
        if(test != nil)
            [test release];
        test = [_test retain];
    }

    // if(test == nil)
    //     test = @"Initialize the var here if you need to";

    return test;
}

Итак, если вам нужно получить значение, просто позвоните:

NSString *testVal = [MyClass testHolder:nil]

И тогда, когда вы хотите установить его:

[MyClass testHolder:testVal]

Если вы хотите установить псевдостатическую переменную равной nil, вы можете объявить testHolderтак:

+ (NSString*)testHolderSet:(BOOL)shouldSet newValue:(NSString*)_test {
    static NSString *test;

    if(shouldSet) {
        if(test != nil)
            [test release];
        test = [_test retain];
    }

    return test;
}

И два удобных метода:

+ (NSString*)test {
    return [MyClass testHolderSet:NO newValue:nil];
}

+ (void)setTest:(NSString*)_test {
    [MyClass testHolderSet:YES newValue:_test];
}

Надеюсь, поможет! Удачи.


Круто, но на самом деле это не глобальная переменная, потому что к ней нельзя получить доступ из других .mфайлов, и я думаю, что это нормально, чтобы она была "глобальной" внутри Class.mфайла.
ma11hew28

29

В вашем файле .m вы можете объявить переменную как статическую:

static ClassName *variableName = nil;

Затем вы можете инициализировать его по своему +(void)initializeметоду.

Обратите внимание, что это обычная статическая переменная C, которая не является статической в ​​том смысле, в каком ее считают Java или C #, но даст аналогичные результаты.


16

В вашем файле .m объявите глобальную переменную файла:

static int currentID = 1;

затем в вашей процедуре инициализации, что ссылка:

- (id) init
{
    self = [super init];
    if (self != nil) {
        _myID = currentID++; // not thread safe
    }
    return self;
}

или если это нужно изменить в другое время (например, в вашем методе openConnection), то увеличьте его там. Помните, что он не является потокобезопасным, как есть, вам нужно будет выполнить синхронизацию (или, что еще лучше, использовать атомарное добавление), если могут возникнуть какие-либо проблемы с потоками.


11

Как сказал pgb, здесь нет «переменных класса», только «переменные экземпляра». Метод target-c для работы с переменными класса - это статическая глобальная переменная внутри файла .m класса. «Статический» гарантирует, что переменная не может быть использована вне этого файла (то есть она не может быть внешней).


3

Здесь будет вариант:

+(int)getId{
    static int id;
    //Do anything you need to update the ID here
    return id;
}

Обратите внимание, что этот метод будет единственным методом доступа к идентификатору, поэтому вам придется как-то обновить его в этом коде.


2

(Строго говоря, не ответ на вопрос, но по моему опыту, вероятно, будет полезен при поиске переменных класса)

Метод класса часто может играть многие роли, которые переменная класса играет в других языках (например, изменение конфигурации во время тестов):

@interface MyCls: NSObject
+ (NSString*)theNameThing;
- (void)doTheThing;
@end
@implementation
+ (NSString*)theNameThing { return @"Something general"; }
- (void)doTheThing {
  [SomeResource changeSomething:[self.class theNameThing]];
}
@end

@interface MySpecialCase: MyCls
@end
@implementation
+ (NSString*)theNameThing { return @"Something specific"; }
@end

Теперь объект класса MyClsвызывает Resource:changeSomething:со строкой @"Something general"при вызове doTheThing:, но объект, полученный из MySpecialCaseэтой строки @"Something specific".


0

Вы можете переименовать класс в classA.mm и добавить в него функции C ++.


Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.