Wie deklariere ich Eigenschaften auf Klassenebene in Objective-C?


205

Vielleicht ist das offensichtlich, aber ich weiß nicht, wie man Klasseneigenschaften in Objective-C deklariert.

Ich muss ein Wörterbuch pro Klasse zwischenspeichern und mich fragen, wie ich es in die Klasse einfügen soll.

Antworten:


190

Eigenschaften haben in Objective-C eine bestimmte Bedeutung, aber ich denke, Sie meinen etwas, das einer statischen Variablen entspricht? ZB nur eine Instanz für alle Arten von Foo?

Um Klassenfunktionen in Objective-C zu deklarieren, verwenden Sie das Präfix + anstelle von - Ihre Implementierung sieht also ungefähr so ​​aus:

// Foo.h
@interface Foo {
}

+ (NSDictionary *)dictionary;

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

23
Ist das richtig? Wird fooDict nicht auf Null gesetzt, da die erste Zeile der Wörterbuchmethode immer dazu führt, dass das Wörterbuch jedes Mal neu erstellt wird?
Papillon


59
Die Zeile statisches NSDictionary * fooDict = nil; wird nur einmal ausgeführt! Selbst in einer mehrfach aufgerufenen Methode wird die Deklaration (und in diesem Beispiel auch die Initialisierung) mit dem Schlüsselwort static ignoriert, wenn eine statische Variable mit diesem Namen vorhanden ist.
Binarian

3
@ BenC.R.Leggiero Ja, absolut. Die .Syntax -accessor ist nicht an Eigenschaften in Objective-C gebunden, sondern lediglich eine kompilierte Verknüpfung für jede Methode, die etwas zurückgibt, ohne Argumente zu verwenden. In diesem Fall würde ich es vorziehen - ich persönlich bevorzuge die .Syntax für jede Verwendung, bei der der Client-Code etwas abrufen und keine Aktion ausführen möchte (selbst wenn der Implementierungscode möglicherweise einmal etwas erstellt oder Nebenwirkungen ausführt) . Eine .Syntax mit starker Nutzung führt auch zu besser lesbarem Code: Das Vorhandensein von […]s bedeutet, dass etwas Bedeutendes getan wird, wenn Abrufe .stattdessen Syntax verwenden.
Slipp D. Thompson

4
Werfen Sie einen Blick auf Alex Nolascos Antwort: Klasseneigenschaften sind seit der Veröffentlichung von Xcode 8 verfügbar: stackoverflow.com/a/37849467/6666611
n3wbie

112

Ich benutze diese Lösung:

@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

Und ich fand es äußerst nützlich als Ersatz für das Singleton-Muster.

Um es zu verwenden, greifen Sie einfach mit Punktnotation auf Ihre Daten zu:

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

3
Das ist wirklich cool. Aber was um alles in der Welt selfbedeutet innerhalb einer Klassenmethode?
Todd Lehman

8
@ToddLehman selfist das Objekt, das die Nachricht empfangen hat . Und da Klassen sind auch Objekte , in diesem Fall selfMittelModel
Spooki

6
Warum sollte der Getter sein müssen @synchronized?
Matt Kantor

1
Das ist eigentlich ziemlich cool, dass das funktioniert. Dass Sie Ihre eigenen Eigenschaften auf Klassenebene schreiben können, die genau wie das Original funktionieren. Ich denke, das Synchronisieren von Selbst entspricht der Verwendung von 'atomic' in Ihrer Eigenschaftsdeklaration und kann weggelassen werden, wenn Sie die 'nonatomic'-Version möchten. Außerdem würde ich in Betracht ziehen, die Hintergrundvariablen mit '_' zu benennen, wie es bei Apple standardmäßig der Fall ist, und die Lesbarkeit zu verbessern, da die Rückgabe / Einstellung von self.value vom Getter / Setter eine unendliche Rekursion verursacht. Meiner Meinung nach ist dies die richtige Antwort.
Peter Segerblom

3
Es ist cool, aber ... 10 Codezeilen, nur um 1 statisches Mitglied zu erstellen? Was für ein Hack. Apple sollte dies nur zu einer Funktion machen.
John Henckel

91

Wie in WWDC 2016 / XCode 8 zu sehen ( was ist neu in der LLVM-Sitzung um 5: 05). Klasseneigenschaften können wie folgt deklariert werden

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

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

Beachten Sie, dass Klasseneigenschaften niemals synthetisiert werden

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

7
Es sollte vielleicht explizit gemacht werden, dass dies nur Zucker für die Accessor-Erklärungen ist. Wie Sie bereits bemerkt haben, wird die Eigenschaft nicht synthetisiert: Eine ( static) Variable muss weiterhin deklariert und verwendet werden, und die Methoden müssen explizit wie zuvor implementiert werden. Die Punktsyntax hat auch schon früher funktioniert. Alles in allem klingt dies nach einer größeren Sache als es wirklich ist.
Jscs

5
Die große Sache ist, dass Sie ohne Verwendung von () über Swift-Code auf Singletons zugreifen können und das Typensuffix gemäß Konvention entfernt wird. ZB XYZMyClass.shared (Swift 3) anstelle von XYZMyClass.sharedMyClass ()
Ryan

Das sieht nicht threadsicher aus. Wenn dies von verschiedenen Threads in Ihrem Code mutiert werden könnte, würde ich sicherstellen, dass Sie mit der potenziellen Race-Bedingung umgehen, die dadurch entstehen würde.
smileBot

Eine schöne, saubere Oberfläche für den Unterricht, aber es ist immer noch eine Menge Arbeit. Versuchte dies mit einem statischen Block und es war nicht viel Spaß. Tatsächlich war es viel einfacher, nur die Statik zu verwenden.
Departamento B

1
Die Implementierung kann leicht für das thread-sichere "Singleton" -Verhalten mithilfe des Dispatch_once-Tokens verbessert werden. Andernfalls zeigt die Lösung die Antwort korrekt
Motti Shneor,

63

Wenn Sie nach dem Äquivalent auf Klassenebene suchen @property, lautet die Antwort "Es gibt so etwas nicht". Aber denken Sie daran, @propertyist sowieso nur syntaktischer Zucker; Es werden nur Objektmethoden mit entsprechendem Namen erstellt.

Sie möchten Klassenmethoden erstellen, die auf statische Variablen zugreifen, die, wie andere gesagt haben, nur eine geringfügig andere Syntax haben.


Auch wenn die Eigenschaften syntaktisch sind, wäre es immer noch schön, die Punktsyntax für Dinge wie MyClass.class anstelle von [MyClass class] verwenden zu können.
Zaky German

4
@ZakyGerman Du kannst! UIDevice.currentDevice.identifierForVendorfunktioniert bei mir.
tc.

1
@tc. Danke dir! Jetzt albern füllen. Aus irgendeinem Grund war ich überzeugt, dass ich das in der Vergangenheit versucht habe, aber es hat nicht funktioniert. Ist das zufällig eine neue Funktion?
Zaky German

1
@ZakyGerman Es hat mindestens ein oder zwei Jahre für Klassenmethoden gearbeitet. Ich glaube, es hat zum Beispiel immer funktioniert, wenn die Getter / Setter-Methoden die erwarteten Typen haben.
tc.

21

Hier ist eine thread-sichere Methode:

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

Diese Änderungen stellen sicher, dass fooDict nur einmal erstellt wird.

Aus der Apple-Dokumentation : "dispatch_once - Führt ein Blockobjekt einmal und nur einmal für die Lebensdauer einer Anwendung aus."


3
Ist der dispatch_once-Code nicht irrelevant, da ein statisches NSDictionary in der ersten Zeile der Wörterbuchmethode + (NSDictionary *) initialisiert werden kann und da es statisch ist, wird es sowieso nur einmal initialisiert?
JCPennypincher

@jcpennypincher Der Versuch, das Wörterbuch in derselben Zeile wie seine statische Deklaration zu initialisieren, führt zu folgendem Compilerfehler : Initializer element is not a compile-time constant.
George WS

@GeorgeWS Dieser Fehler wird nur angezeigt, weil Sie versuchen, ihn mit dem Ergebnis einer Funktion zu initialisieren (alloc und init sind Funktionen). Wenn Sie es auf nil initialisieren und dann das if (obj == nil) hinzufügen und dort initialisieren, ist alles in Ordnung.
Rob

1
Rob, das ist nicht threadsicher. Der hier vorgestellte Code ist am besten.
Ian Ollmann

Diese Antwort gefällt mir am besten, weil sie vollständig und threadsicher ist - sie behandelt jedoch nur die Implementierung besser, während die Frage der Operation die Deklaration von Klasseneigenschaften betraf - nicht deren Implementierung (was nichts mit der Implementierung zu tun hat). Danke trotzdem.
Motti Shneor

11

Ab Xcode 8 unterstützt Objective-C jetzt Klasseneigenschaften:

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

Da Klasseneigenschaften niemals synthetisiert werden, müssen Sie Ihre eigene Implementierung schreiben.

@implementation MyClass
static NSUUID*_identifier = nil;

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

Sie greifen auf die Klasseneigenschaften mit der normalen Punktsyntax für den Klassennamen zu:

MyClass.identifier;

7

Eigenschaften haben Werte nur in Objekten, nicht in Klassen.

Wenn Sie etwas für alle Objekte einer Klasse speichern müssen, müssen Sie eine globale Variable verwenden. Sie können es ausblenden, indem Sie es staticin der Implementierungsdatei deklarieren .

Sie können auch bestimmte Beziehungen zwischen Ihren Objekten verwenden: Sie weisen einem bestimmten Objekt Ihrer Klasse eine Masterrolle zu und verknüpfen andere Objekte mit diesem Master. Der Master hält das Wörterbuch als einfache Eigenschaft. Ich stelle mir einen Baum vor, wie er für die Ansichtshierarchie in Cocoa-Anwendungen verwendet wird.

Eine andere Möglichkeit besteht darin, ein Objekt einer dedizierten Klasse zu erstellen, das sowohl aus Ihrem Klassenwörterbuch als auch aus einer Reihe aller mit diesem Wörterbuch verbundenen Objekte besteht. Das ist so etwas wie NSAutoreleasePoolin Kakao.


7

Ab Xcode 8 können Sie das von Berbie beantwortete Klasseneigenschaftsattribut verwenden .

In der Implementierung müssen Sie jedoch sowohl den Klassen-Getter als auch den Setter für die Klasseneigenschaft mithilfe einer statischen Variablen anstelle eines iVar definieren.

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

Wenn Sie viele Eigenschaften auf Klassenebene haben, ist möglicherweise ein Singleton-Muster in Ordnung. Etwas wie das:

// Foo.h
@interface Foo

+ (Foo *)singleton;

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

@end

Und

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

Greifen Sie jetzt wie folgt auf Ihre Eigenschaften auf Klassenebene zu:

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

4
Diese Singleton-Implementierung ist nicht threadsicher, verwenden Sie sie nicht
klefevre

1
Natürlich ist es nicht threadsicher, Sie sind die erste Person, die die Thread-Sicherheit erwähnt, und standardmäßig macht die Überlastung der Thread-Sicherheit in nicht thread-sicheren Kontexten, dh einem einzelnen Thread, keinen Sinn.
Pedro Borges

Es wäre ziemlich einfach, dispatch_oncehier ein zu verwenden.
Ian MacDonald

Die PO wollte eine Antwort auf der Seite der Erklärung, nicht auf die Implementierung - und selbst die vorgeschlagene Implementierung ist unvollständig (nicht threadsicher).
Motti Shneor

-3

[Probieren Sie diese Lösung einfach aus] Sie können eine statische Variable in einer Swift-Klasse erstellen und sie dann von jeder Objective-C-Klasse aus aufrufen.


1
OP hat nicht gefragt, wie eine statische Eigenschaft in Swift erstellt werden soll. Dies löst sein Problem nicht.
Nathan F.

Oder besser gesagt, es hat das Problem gelöst, aber die Frage nicht beantwortet. Ich
bin
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.