Come posso dichiarare le proprietà a livello di classe in Objective-C?


205

Forse questo è ovvio, ma non so come dichiarare le proprietà della classe in Objective-C.

Devo memorizzare nella cache per classe un dizionario e chiedermi come inserirlo nella classe.

Risposte:


190

le proprietà hanno un significato specifico in Objective-C, ma penso che intendi qualcosa che equivale a una variabile statica? Ad esempio solo un'istanza per tutti i tipi di Foo?

Per dichiarare le funzioni di classe in Objective-C si usa il prefisso + invece di - quindi l'implementazione sarebbe simile a:

// Foo.h
@interface Foo {
}

+ (NSDictionary *)dictionary;

// Foo.m
+ (NSDictionary *)dictionary {
  static NSDictionary *fooDict = nil;
  if (fooDict == nil) {
    // create dict
  }
  return fooDict;
}

23
È giusto? L'impostazione di fooDict su zero come prima riga del metodo del dizionario comporta sempre la ricreazione del dizionario ogni volta?
Papillon,


59
La linea statica NSDictionary * fooDict = nil; verrà eseguito solo una volta! Anche in un metodo chiamato più volte, la dichiarazione (e anche in questo esempio l'inizializzazione) con la parola chiave static verrà ignorata se esiste una variabile statica con questo nome.
Binarian

3
@ BenC.R.Leggiero Sì, assolutamente. La .sintassi -accessor non è legata alle proprietà in Objective-C, è solo una scorciatoia compilata per qualsiasi metodo che restituisce qualcosa senza prendere argomenti. In questo caso lo preferirei: preferisco personalmente la .sintassi per qualsiasi utilizzo in cui il codice client intende ottenere qualcosa, non eseguire un'azione (anche se il codice di implementazione può creare qualcosa una volta o eseguire azioni con effetti collaterali) . La .sintassi per un uso intenso comporta anche un codice più leggibile: la presenza di […]s significa che si sta facendo qualcosa di significativo quando si recupera .invece la sintassi.
Slipp D. Thompson,

4
Dai un'occhiata alla risposta di Alex Nolasco, proprietà della classe sono disponibili a partire dal rilascio Xcode 8: stackoverflow.com/a/37849467/6666611
n3wbie

113

Sto usando questa soluzione:

@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 l'ho trovato estremamente utile in sostituzione del modello Singleton.

Per usarlo, accedi semplicemente ai tuoi dati con la notazione a punti:

Model.value = 1;
NSLog(@"%d = value", Model.value);

3
È davvero fantastico. Ma cosa diavolo selfsignifica all'interno di un metodo di classe?
Todd Lehman,

8
@ToddLehman selfè l'oggetto che ha ricevuto il messaggio . E poiché anche le classi sono oggetti , in questo caso selfsignificaModel
spooki

6
Perché dovrebbe essere il getter @synchronized?
Matt Kantor,

1
In realtà è abbastanza bello che funzioni. Che puoi modificare le tue proprietà a livello di classe che funzionano esattamente come le cose reali. Immagino che sincronizzare sé sia ​​l'equivalente dell'uso di "atomico" nella dichiarazione di proprietà e può essere omesso se si desidera la versione "non anatomica"? Vorrei anche prendere in considerazione la denominazione delle variabili di supporto con '_' come è predefinito da Apple e migliora anche la leggibilità poiché il ritorno / impostazione di self.value dal getter / setter provoca una ricorsione infinita. Secondo me questa è la risposta giusta.
Peter Segerblom,

3
È bello, ma ... 10 righe di codice solo per creare 1 membro statico? che hack. Apple dovrebbe solo renderlo una funzionalità.
John Henckel,

92

Come visto in WWDC 2016 / XCode 8 ( novità della sessione LLVM @ 5: 05). Le proprietà della classe possono essere dichiarate come segue

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

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

Si noti che le proprietà della classe non vengono mai sintetizzate

@implementation
static NSString * _someString;
+ (NSString *)someString { return _someString; }
+ (void)setSomeString:(NSString *)newString { _someString = newString; }
@end

8
Dovrebbe forse essere chiarito che si tratta solo di zucchero per le dichiarazioni degli accedenti. Come hai notato, la proprietà non è sintetizzata: una staticvariabile ( ) deve ancora essere dichiarata e utilizzata e i metodi implementati esplicitamente, proprio come in precedenza. La sintassi del punto ha già funzionato anche in precedenza. In tutto, sembra un affare più grande di quello che è in realtà.
jscs,

6
Il grosso problema è che questo significa che puoi accedere ai singoli dal codice Swift senza usare (), e il suffisso del tipo viene rimosso per convenzione. Ad esempio XYZMyClass.shared (Swift 3) invece di XYZMyClass.sharedMyClass ()
Ryan,

Questo non sembra sicuro. Se questo potesse essere mutato da diversi thread nel tuo codice, mi assicurerei di gestire le potenziali condizioni di gara che ciò creerebbe.
smileBot,

Una bella interfaccia pulita per le classi che consumano ma è ancora un sacco di lavoro. Ho provato questo con un blocco statico e non è stato molto divertente. In effetti, solo usare la statica era molto più semplice.
Departamento B,

1
l'implementazione può essere facilmente migliorata per un comportamento "singleton" thread-safe, usando il token dispatch_once - ma per il resto la soluzione dimostra correttamente la risposta
Motti Shneor,

63

Se stai cercando l'equivalente a livello di classe @property, allora la risposta è "non esiste nulla di simile". Ma ricorda, @propertyè solo zucchero sintattico, comunque; crea solo metodi oggetto con nomi appropriati.

Volete creare metodi di classe che accedano a variabili statiche che, come altri hanno già detto, hanno solo una sintassi leggermente diversa.


Anche se le proprietà del pensiero sono sintattiche Sarebbe comunque bello poter usare la sintassi del punto per cose come MyClass.class invece che [classe MyClass].
Zaky German,

4
@ZakyGerman Puoi! UIDevice.currentDevice.identifierForVendorper me va bene.
tc.

1
@tc. Grazie! Riempimento sciocco ora. Per qualche motivo, ero convinto di averlo provato in passato ma non ha funzionato. È una nuova funzionalità per caso?
Zaky German,

1
@ZakyGerman Ha funzionato per almeno un anno o due per i metodi di classe. Credo che abbia sempre funzionato per esempio i metodi se i metodi getter / setter hanno i tipi previsti.
tc.

21

Ecco un modo sicuro per farlo:

// 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;
}

Queste modifiche assicurano che fooDict sia creato una sola volta.

Dalla documentazione Apple : "dispatch_once - Esegue un oggetto blocco una e una sola volta per la durata di un'applicazione".


3
Il codice dispatch_once non è irrilevante poiché un NSDictionary statico potrebbe essere inizializzato sulla prima riga del metodo del dizionario + (NSDictionary *) e poiché è statico verrà inizializzato solo una volta?
jcpennypincher,

@jcpennypincher Il tentativo di inizializzare il dizionario sulla stessa linea, come la sua dichiarazione statica produce il seguente errore del compilatore: Initializer element is not a compile-time constant.
George WS,

@GeorgeWS Stai ricevendo questo errore solo perché stai tentando di inizializzarlo al risultato di una funzione (alloc e init sono funzioni). Se lo inizializzi a zero, quindi aggiungi if (obj == nil) e inizializzi lì, starai bene.
Rob,

1
Rob, non è sicuro per i thread. Il codice presentato qui è il migliore.
Ian Ollmann,

Mi piace molto questa risposta, perché è completa e sicura per i thread - tuttavia, tratta solo meglio dell'implementazione, mentre la domanda dell'op riguardava la dichiarazione delle proprietà della classe - non la loro implementazione (che non ha nulla a che fare con l'implementazione). Grazie comunque.
Motti Shneor,

11

A partire da Xcode 8 Objective-C ora supporta le proprietà di classe:

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

Poiché le proprietà della classe non vengono mai sintetizzate, è necessario scrivere la propria implementazione.

@implementation MyClass
static NSUUID*_identifier = nil;

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

Si accede alle proprietà della classe usando la normale sintassi del punto sul nome della classe:

MyClass.identifier;

7

Le proprietà hanno valori solo negli oggetti, non nelle classi.

Se devi archiviare qualcosa per tutti gli oggetti di una classe, devi utilizzare una variabile globale. Puoi nasconderlo dichiarandolo staticnel file di implementazione.

Puoi anche considerare l'utilizzo di relazioni specifiche tra i tuoi oggetti: attribuisci un ruolo di maestro a un oggetto specifico della tua classe e colleghi altri oggetti a questo maestro. Il master manterrà il dizionario come una semplice proprietà. Penso a un albero come quello usato per la gerarchia delle viste nelle applicazioni Cocoa.

Un'altra opzione è quella di creare un oggetto di una classe dedicata composto sia dal tuo dizionario di "classe" sia da un insieme di tutti gli oggetti relativi a questo dizionario. Questo è qualcosa di simile NSAutoreleasePoolal cacao.


7

A partire da Xcode 8, è possibile utilizzare l' attributo della proprietà class come risposto da Berbie.

Tuttavia, nell'implementazione, è necessario definire sia il getter di classe che il setter per la proprietà della classe utilizzando una variabile statica al posto di un 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

2

Se si dispone di molte proprietà a livello di classe, potrebbe essere in ordine un modello singleton. Qualcosa come questo:

// 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

Ora accedi alle tue proprietà a livello di classe come questa:

[Foo singleton].property1 = value;
value = [Foo singleton].property2;

4
Questa implementazione singleton non è thread-safe, non usarla
klefevre

1
Ovviamente non è sicuro per i thread, sei la prima persona a menzionare la sicurezza dei thread e, per impostazione predefinita, non ha senso il sovraccarico della sicurezza dei thread in contesti non thread-safe, ad esempio thread singolo.
Pedro Borges,

Sarebbe abbastanza facile usare un dispatch_oncequi.
Ian MacDonald,

L'OP ha voluto una risposta dal lato della dichiarazione, non dell'implementazione, e anche l'implementazione suggerita è incompleta (non sicura per i thread).
Motti Shneor,

-3

[Prova questa soluzione è semplice] Puoi creare una variabile statica in una classe Swift e chiamarla da qualsiasi classe Objective-C.


1
OP non ha chiesto come creare una proprietà statica in Swift, questo non risolve il suo problema.
Nathan F.

O meglio, ha risolto il problema, ma non ha risposto alla domanda. Non sono sicuro se questo merita e vota verso il basso ...
AmitaiB
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.