Como declaro propriedades no nível da classe no Objective-C?

205

Talvez isso seja óbvio, mas não sei como declarar propriedades de classe no Objective-C.

Preciso armazenar em cache um dicionário por classe e me perguntar como colocá-lo na classe.

mamcx
fonte

Respostas:

190

propriedades têm um significado específico em Objective-C, mas acho que você quer dizer algo equivalente a uma variável estática? Por exemplo, apenas uma instância para todos os tipos de Foo?

Para declarar funções de classe no Objective-C, use o prefixo + em vez de - para que sua implementação se pareça com:

// Foo.h
@interface Foo {
}

+ (NSDictionary *)dictionary;

// Foo.m
+ (NSDictionary *)dictionary {
  static NSDictionary *fooDict = nil;
  if (fooDict == nil) {
    // create dict
  }
  return fooDict;
}
Andrew Grant
fonte
23
Isto está certo? Definir fooDict como nulo como a primeira linha do método do dicionário sempre resulta na recriação do dicionário a cada vez?
21412 PapillonUK #
59
A linha estática NSDictionary * fooDict = nil; será executado apenas uma vez! Mesmo em um método chamado várias vezes, a declaração (e também neste exemplo a inicialização) com a palavra-chave static será ignorada se existir uma variável estática com esse nome.
Binarian 14/08/13
3
@ BenC.R.Leggiero Sim, absolutamente. A .sintaxe -accessor não está vinculada a propriedades no Objective-C, é apenas um atalho compilado para qualquer método que retorna algo sem aceitar argumentos. Nesse caso, eu preferiria - eu pessoalmente prefiro a .sintaxe para qualquer uso em que o código do cliente pretenda obter algo, não execute uma ação (mesmo que o código de implementação possa criar algo uma vez ou executar ações de efeito colateral) . A .sintaxe de uso intenso também resulta em código mais legível: a presença de […]s significa que algo significativo está sendo feito quando as buscas usam a .sintaxe.
Slipp D. Thompson
4
Ter um olhar para a resposta de Alex Nolasco, propriedades de classe estão disponíveis desde o lançamento Xcode 8: stackoverflow.com/a/37849467/6666611
n3wbie
113

Estou usando esta solução:

@interface Model
+ (int) value;
+ (void) setValue:(int)val;
@end

@implementation Model
static int value;
+ (int) value
{ @synchronized(self) { return value; } }
+ (void) setValue:(int)val
{ @synchronized(self) { value = val; } }
@end

E eu achei extremamente útil como uma substituição do padrão Singleton.

Para usá-lo, basta acessar seus dados com notação de ponto:

Model.value = 1;
NSLog(@"%d = value", Model.value);
spooki
fonte
3
Isso é muito legal. Mas o que diabos selfsignifica dentro de um método de classe?
Todd Lehman
8
@ToddLehman selfé o objeto que recebeu a mensagem . E uma vez que as aulas são também objetos , neste caso selfmeiosModel
spooki
6
Por que o getter precisaria ser @synchronized?
Matt Kantor
1
É realmente muito legal que isso funcione. Que você pode criar suas próprias propriedades de nível de classe que funcionam exatamente como a coisa real. Eu acho que sincronizar a si mesmo é equivalente a usar 'atômico' na sua declaração de propriedade e pode ser omitido se você quiser a versão 'não atômica'? Também consideraria nomear as variáveis ​​de suporte com '_' como padrão da apple e também melhora a legibilidade, pois o retorno / configuração do self.value do getter / setter causa recursão infinita. Na minha opinião, esta é a resposta certa.
Peter Segerblom
3
É legal, mas ... 10 linhas de código apenas para criar um membro estático? que truque. A Apple deve apenas fazer disso um recurso.
21714 John Henckel
92

Como visto na WWDC 2016 / XCode 8 (o que há de novo na sessão do LLVM às 05:00 ). Propriedades da classe podem ser declaradas da seguinte maneira

@interface MyType : NSObject
@property (class) NSString *someString;
@end

NSLog(@"format string %@", MyType.someString);

Observe que as propriedades da classe nunca são sintetizadas

@implementation
static NSString * _someString;
+ (NSString *)someString { return _someString; }
+ (void)setSomeString:(NSString *)newString { _someString = newString; }
@end
Alex Nolasco
fonte
8
Talvez devesse ficar explícito que isso é apenas açúcar para as declarações do acessador. Como você observou, a propriedade não é sintetizada: uma staticvariável ( ) ainda deve ser declarada e usada, e os métodos implementados explicitamente, exatamente como eram anteriormente. A sintaxe de pontos também já funcionou anteriormente. Ao todo, isso parece um negócio maior do que realmente é.
JSCs
6
O importante é que isso significa que você pode acessar singletons a partir do código Swift sem usar (), e o sufixo do tipo é removido por convenção. Por exemplo, XYZMyClass.shared (Swift 3) em vez de XYZMyClass.sharedMyClass ()
Ryan
Isso não parece seguro para threads. Se isso puder ser modificado a partir de threads diferentes no seu código, garantiria que você lida com a condição de corrida em potencial que isso criaria.
smileBot
Uma interface limpa e agradável para consumir aulas, mas ainda é muito trabalho. Tentei isso com um bloco estático e não foi muito divertido. De fato, apenas o uso da estática foi muito mais fácil.
Departamento B
1
a implementação pode ser facilmente melhorado para thread-safe comportamento "singleton", usando dispatch_once simbólico - mas por outro lado a solução demonstra corretamente a resposta
Motti Shneor
63

Se você está procurando o equivalente a nível de classe @property, a resposta é "não existe". Mas lembre-se, @propertyé apenas açúcar sintático, de qualquer maneira; apenas cria métodos de objeto com nome apropriado.

Você deseja criar métodos de classe que acessem variáveis ​​estáticas que, como outros já disseram, têm apenas uma sintaxe ligeiramente diferente.

Jim Puls
fonte
Até as propriedades pensadas são sintáticas. Ainda assim, seria bom poder usar a sintaxe de pontos para coisas como MyClass.class em vez de [classe MyClass].
Zaky German
4
@ZakyGerman Você pode! UIDevice.currentDevice.identifierForVendorfunciona para mim.
tc.
1
@tc. Obrigado! Enchendo bobo agora. Por alguma razão, eu estava convencido de que havia tentado isso no passado, mas não funcionou. Esse é um novo recurso por acaso?
Zaky German 10/11/12
1
@ZakyGerman Ele trabalhou por pelo menos um ano ou dois para métodos de classe. Acredito que sempre funcionou, por exemplo, métodos, se os métodos getter / setter tiverem os tipos esperados.
tc.
21

Aqui está uma maneira segura de fazer isso:

// Foo.h
@interface Foo {
}

+(NSDictionary*) dictionary;

// Foo.m
+(NSDictionary*) dictionary
{
  static NSDictionary* fooDict = nil;

  static dispatch_once_t oncePredicate;

  dispatch_once(&oncePredicate, ^{
        // create dict
    });

  return fooDict;
}

Essas edições garantem que fooDict seja criado apenas uma vez.

Na documentação da Apple : "dispatch_once - executa um objeto de bloco uma vez e apenas uma vez durante a vida útil de um aplicativo".

Quentin
fonte
3
O código dispatch_once não é irrelevante, pois um NSDictionary estático pode ser inicializado na primeira linha do método de dicionário + (NSDictionary *) e, como estático, ele será inicializado apenas uma vez?
jcpennypincher
@jcpennypincher tentativa de inicializar o dicionário na mesma linha que a sua declaração estática produz o seguinte erro do compilador: Initializer element is not a compile-time constant.
George WS
@ GeorgeorgeWS Você está recebendo esse erro apenas porque está tentando inicializá-lo para o resultado de uma função (alocação e init são funções). Se você inicializá-lo como nulo e, em seguida, adicionar o if (obj == nil) e inicializá-lo, tudo ficará bem.
26714 Rob
1
Rob, isso não é seguro para discussão. O código apresentado aqui é o melhor.
Ian Ollmann 27/05
Eu gosto mais dessa resposta, porque ela é completa e segura para threads - no entanto, ela lida apenas melhor com a implementação, enquanto a pergunta do op era sobre a declaração das propriedades da classe - não sobre a implementação (que não tem nada a ver com a implementação). Obrigado mesmo assim.
Motti Shneor
11

A partir do Xcode 8, o Objective-C agora suporta propriedades de classe:

@interface MyClass : NSObject
@property (class, nonatomic, assign, readonly) NSUUID* identifier;
@end

Como as propriedades da classe nunca são sintetizadas, você precisa escrever sua própria implementação.

@implementation MyClass
static NSUUID*_identifier = nil;

+ (NSUUID *)identifier {
  if (_identifier == nil) {
    _identifier = [[NSUUID alloc] init];
  }
  return _identifier;
}
@end

Você acessa as propriedades da classe usando a sintaxe de ponto normal no nome da classe:

MyClass.identifier;
Berbie
fonte
7

Propriedades têm valores apenas em objetos, não em classes.

Se você precisar armazenar algo para todos os objetos de uma classe, precisará usar uma variável global. Você pode ocultá-lo declarando-o staticno arquivo de implementação.

Você também pode considerar o uso de relações específicas entre seus objetos: atribui uma função de mestre a um objeto específico da sua classe e vincula outros objetos a esse mestre. O mestre manterá o dicionário como uma propriedade simples. Penso em uma árvore como a usada para a hierarquia de exibição nos aplicativos de cacau.

Outra opção é criar um objeto de uma classe dedicada composta pelo dicionário da 'classe' e por um conjunto de todos os objetos relacionados a este dicionário. Isso é algo como NSAutoreleasePoolno cacau.

mouviciel
fonte
7

A partir do Xcode 8, você pode usar o atributo de propriedade de classe conforme respondido por Berbie.

No entanto, na implementação, você precisa definir o getter e o setter de classe para a propriedade de classe usando uma variável estática no lugar de um iVar.

Sample.h

@interface Sample: NSObject
@property (class, retain) Sample *sharedSample;
@end

Sample.m

@implementation Sample
static Sample *_sharedSample;
+ ( Sample *)sharedSample {
   if (_sharedSample==nil) {
      [Sample setSharedSample:_sharedSample];
   }
   return _sharedSample;
}

+ (void)setSharedSample:(Sample *)sample {
   _sharedSample = [[Sample alloc]init];
}
@end
Jean-Marie D.
fonte
2

Se você tiver muitas propriedades no nível de classe, um padrão singleton poderá estar em ordem. Algo assim:

// Foo.h
@interface Foo

+ (Foo *)singleton;

@property 1 ...
@property 2 ...
@property 3 ...

@end

E

// Foo.m

#import "Foo.h"

@implementation Foo

static Foo *_singleton = nil;

+ (Foo *)singleton {
    if (_singleton == nil) _singleton = [[Foo alloc] init];

    return _singleton;
}

@synthesize property1;
@synthesize property2;
@synthesise property3;

@end

Agora acesse suas propriedades em nível de classe como esta:

[Foo singleton].property1 = value;
value = [Foo singleton].property2;
Pedro Borges
fonte
4
Esta implementação singleton não é thread-safe, não usá-lo
klefevre
1
É claro que não é seguro para threads, você é a primeira pessoa a mencionar a segurança de threads e, por padrão, não faz sentido a sobrecarga de segurança de threads em contextos que não são seguros, como thread único.
Pedro Borges
Seria muito fácil usar um dispatch_onceaqui.
Ian MacDonald
O PO queria uma resposta do lado da Declaração, não a implementação - e até a implementação sugerida está incompleta (não é segura para threads).
Motti Shneor
-3

[Tente esta solução, é simples] Você pode criar uma variável estática em uma classe Swift e chamá-la de qualquer classe Objective-C.

jawad
fonte
1
O OP não perguntou como criar uma propriedade estática no Swift, isso não resolve o problema dele.
Nathan F.
Ou melhor, resolveu o problema, mas não respondeu à pergunta. Não tenho certeza se isso merece e vote para baixo ...
AmitaiB 1/19/19