Variáveis ​​de nível de classe estática Objective-C

143

Eu tenho uma classe Film, cada uma das quais armazena um ID único. Em C #, Java, etc, posso definir um int staticID estático e toda vez que eu definir o ID, posso aumentar o currentID e a alteração ocorre no nível da classe e não no nível do objeto. Isso pode ser feito no Objective-C? Achei muito difícil encontrar uma resposta para isso.

Cœur
fonte

Respostas:

158

Descrição do problema :

  1. Você deseja que sua ClassA tenha uma variável de classe ClassB.
  2. Você está usando o Objective-C como linguagem de programação.
  3. O Objective-C não suporta variáveis ​​de classe como o C ++.

Uma alternativa :

Simule um comportamento de variável de classe usando os recursos Objective-C

  1. Declare / defina uma variável estática dentro da classeA.m, para que seja acessível apenas aos métodos da classeA (e tudo o que você colocar dentro da classeA.m).

  2. Substitua o método da classe de inicialização NSObject para inicializar apenas uma vez a variável estática com uma instância da ClassB.

  3. Você deve estar se perguntando, por que devo substituir o método de inicialização NSObject. A documentação da Apple sobre esse método tem a resposta: "O tempo de execução envia a inicialização de cada classe em um programa exatamente uma vez antes da classe, ou qualquer classe que herda dela, recebe sua primeira mensagem de dentro do programa. (Assim, o método nunca pode ser chamado se a classe não for usada.) ".

  4. Sinta-se livre para usar a variável estática em qualquer método de classe / instância ClassA.

Exemplo de código :

arquivo: 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

Referências :

  1. Variáveis ​​de classe explicadas comparando as abordagens Objective-C e C ++
Albaregar
fonte
3
Você pode ter uma variável estática do Tipo ClassA em classA.m?
goatlinks
6
essa pode ser uma pergunta boba, mas e a liberação dessa memória? não importa porque tem que durar tanto tempo que o aplicativo está sendo executado?
samiq
1
@samiq, confira Objective-C: Por que reter uma variável estática? . O ponteiro para o objeto não pode ser excluído, mas o próprio objeto pode. Você provavelmente não deseja liberá-lo porque provavelmente o quer por todo o tempo em que o aplicativo esteja em execução, mas você economizará memória se liberá-lo, por isso, se souber que não precisa mais dele, deverá solte-o.
ma11hew28
5
Se initialize () é garantido para ser chamado apenas uma vez, por que você precisa da condicional "if (! ClassVariableName)"?
jb
23
@jamie, initializeé chamado uma vez para cada classe (superclasses antes das subclasses), mas se uma subclasse não substituir initialize, a classe pai initializeserá chamada novamente. Portanto, um protetor é necessário se você não quiser que esse código seja executado duas vezes. Consulte Inicializando um objeto de classe nos documentos Objective-C da Apple.
big_m
31

No Xcode 8, você pode definir propriedades de classe no Obj-C. Isso foi adicionado para interoperar com as propriedades estáticas do Swift.

O Objective-C agora suporta propriedades de classe, que interoperam com as propriedades do tipo Swift. Eles são declarados como: @property (class) NSString * someStringProperty ;. Eles nunca são sintetizados. (23891898)

Aqui está um exemplo

@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

Então você pode acessá-lo assim:

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

Aqui está um post explicativo muito interessante que usei como referência para editar esta resposta antiga.


Resposta 2011: (não use isso, é terrível)

Se você realmente não quer declarar uma variável global, existe outra opção, talvez não muito ortodoxa :-), mas funciona ... Você pode declarar um método "get & set" como este, com uma variável estática dentro:

+ (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;
}

Portanto, se você precisar obter o valor, basta ligar para:

NSString *testVal = [MyClass testHolder:nil]

E então, quando você quiser configurá-lo:

[MyClass testHolder:testVal]

Caso deseje definir esse pseudo-static-var como nulo, você pode declarar testHoldero seguinte:

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

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

    return test;
}

E dois métodos úteis:

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

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

Espero que ajude! Boa sorte.

Gonzalo Larralde
fonte
Legal, mas não é realmente uma variável global, porque não pode ser acessada de outros .marquivos, e acho que é bom que seja "global" dentro do Class.marquivo.
ma11hew28
29

No seu arquivo .m, você pode declarar uma variável como estática:

static ClassName *variableName = nil;

Então você pode inicializá-lo no seu +(void)initializemétodo.

Observe que essa é uma variável estática C simples e não é estática no sentido que Java ou C # consideram, mas produzirá resultados semelhantes.

pgb
fonte
16

No seu arquivo .m, declare uma variável global do arquivo:

static int currentID = 1;

em sua rotina init, verifique se:

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

ou se precisar mudar em outro momento (por exemplo, no seu método openConnection), então incremente-o lá. Lembre-se de que o thread não é seguro como está; você precisará fazer a sincronização (ou, melhor ainda, usar uma adição atômica), se houver algum problema de encadeamento.

Peter N Lewis
fonte
11

Como pgb disse, não existem "variáveis ​​de classe", apenas "variáveis ​​de instância". A maneira objetiva-c de executar variáveis ​​de classe é uma variável global estática dentro do arquivo .m da classe. O "estático" garante que a variável não possa ser usada fora desse arquivo (ou seja, não pode ser externa).

Tom Dalling
fonte
3

Aqui seria uma opção:

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

Observe que esse método será o único método para acessar a identificação; portanto, você precisará atualizá-lo de alguma forma neste código.

Anônimo
fonte
2

(A rigor, não é uma resposta à pergunta, mas, na minha experiência, provavelmente será útil ao procurar variáveis ​​de classe)

Um método de classe geralmente pode desempenhar muitas das funções que uma variável de classe desempenharia em outros idiomas (por exemplo, configuração alterada durante os testes):

@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

Agora, um objeto da classe MyClschama Resource:changeSomething:com a cadeia de caracteres @"Something general"após uma chamada para doTheThing:, mas um objeto derivado da MySpecialCasecadeia de caracteres @"Something specific".

Jacob Oscarson
fonte
0

Você pode renomear a classe como classA.mm e adicionar recursos C ++ nela.

rd_
fonte
0

Outra possibilidade seria ter um pequeno NSNumbersingleton da subclasse.

Rudolf Adamkovič
fonte