Objective-C sınıf düzeyinde özellikler nasıl bildiririm?


205

Belki bu açıktır, ancak Objective-C sınıf özelliklerini bildirmek için nasıl bilmiyorum.

Sınıf başına bir sözlük önbellek ve nasıl sınıfta koymak merak gerekir.

Yanıtlar:


190

özellikleri Objective-C belirli bir anlamı var, ama statik bir değişken eşdeğer bir şey mi demek istiyorum? Örneğin tüm Foo türleri için sadece bir örnek?

Objective-C sınıf işlevleri bildirmek için yerine + önekini kullanın - böylece uygulamanız şöyle görünecektir:

// Foo.h
@interface Foo {
}

+ (NSDictionary *)dictionary;

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

23
Bu doğru mu? Sözlük yönteminin ilk satırı her zaman sözlüğün yeniden oluşturulmasına neden olacağı için fooDict öğesini nil olarak ayarlamaz mı?
PapillonUK


59
Statik NSDictionary * fooDict = nil; sadece bir kez idam edilecek! Birden çok kez denilen bir yöntemde bile, statik ad anahtar sözcüğüyle yapılan bildirim (ve bu örnekte başlatma) bu adla statik bir değişken varsa yok sayılır.
Binarian

3
@ BenC.R. Laggiero Evet, kesinlikle. .-Accessor sözdizimi Objective-C özelliklerine bağlı değildir, bu sadece bir derlenmiş içinde kısayol herhangi yöntem için herhangi args almadan döner bir şey. Bu durumda tercih ederim - Ben şahsen .müşteri kodunun bir şey almak niyetinde olduğu herhangi bir kullanım için sözdizimini tercih ederim , bir eylem gerçekleştirmeyin (uygulama kodu bir kez bir şeyler yaratabilir veya yan etki eylemleri gerçekleştirse bile) . Ağır kullanım .sözdizimi de daha okunabilir bir kodla sonuçlanır: […]s'nin varlığı, getirmeler .sözdizimi kullandığında önemli bir şeyin yapıldığı anlamına gelir .
Slipp D.Thompson

4
Alex Nolasco'nun cevabına bir göz atın, Xcode 8 sürümünden beri sınıf özellikleri kullanılabilir: stackoverflow.com/a/37849467/6666611
n3wbie

112

Bu çözümü kullanıyorum:

@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

Ve Singleton deseninin yerine son derece yararlı buldum.

Kullanmak için, nokta gösterimi ile verilerinize erişmeniz yeterlidir:

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

3
Bu gerçekten harika. Peki self, sınıf yöntemi içinde yeryüzünde ne anlama geliyor?
Todd Lehman

8
@ToddLehman selfolduğu mesajı Alındıktan nesne . Ve sınıflar da nesne olduğundan, bu durumda selfanlamına gelirModel
spooki

6
Alıcının neden olması gerekir @synchronized?
Matt Kantor

1
Bu gerçekten işe yarıyor çok güzel. Gerçekte olduğu gibi çalışan kendi sınıf düzeyi özelliklerinizi yazabilirsiniz. Ben kendini senkronize özelliği beyannamede 'atom' kullanmaya eşdeğer olduğunu tahmin ve 'anatomik olmayan' sürümü istiyorsanız ihmal edilebilir? Ayrıca elma tarafından varsayılan olarak '_' ile yedekleme değişkenleri adlandırma düşünün ve ayrıca dönen / ayarlayıcı self.value dönen / ayar sonsuz yinelemeye neden olduğundan okunabilirliği artırır. Bence bu doğru cevap.
Peter Segerblom

3
Güzel, ama ... sadece 1 statik üye oluşturmak için 10 kod satırı? ne hack. Apple bunu sadece bir özellik haline getirmelidir.
John Henckel

92

WWDC 2016 / XCode 8'de görüldüğü gibi ( LLVM oturumundaki yenilikler @ 5: 05). Sınıf özellikleri aşağıdaki gibi beyan edilebilir

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

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

Sınıf özelliklerinin asla sentezlenmediğini unutmayın

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

8
Belki de bunun sadece erişimci deklarasyonları için şeker olduğu açıklığa kavuşturulmalıdır . Belirttiğiniz gibi, özellik sentezlenmez: a ( static) değişkeni hala bildirilmeli ve kullanılmalı ve yöntemler daha önce olduğu gibi açıkça uygulanmalıdır. Nokta sözdizimi daha önce de çalıştı. Genel olarak, bu gerçekte olduğundan daha büyük bir anlaşma gibi geliyor.
jscs

6
Önemli olan, Swift kodundan () kullanmadan singletonlara erişebileceğiniz ve tür sonekinin kuralla kaldırıldığı anlamına gelir. Örneğin, XYZMyClass.sharedMyClass () yerine
Ryan

Bu iş parçacığı için güvenli görünmüyor. Bu, kodunuzdaki farklı iş parçacıklarından mutasyona uğramış olsaydı, bu durumun oluşturabileceği olası yarış koşulunu ele aldığınızdan emin olurdum.
smileBot

Sınıfları tüketmek için güzel ve temiz bir arayüz ama yine de bir ton çalışma. Bunu statik bir blokla denedim ve çok eğlenceli değildi. Aslında sadece statik kullanımı çok daha kolaydı.
Departamento B

1
dispatch_once jetonu kullanarak, iş parçacığı güvenli "singleton" davranışı için uygulama kolayca geliştirilebilir - ancak aksi takdirde çözüm doğru bir şekilde yanıtı gösterir
Motti Shneor

63

Eğer sınıf düzeyinde bir eşdeğeri arıyorsanız @property, cevap "böyle bir şey yoktur". Ama unutmayın @property, yine de sadece sözdizimsel şekerdir; yalnızca uygun şekilde adlandırılmış nesne yöntemleri oluşturur.

Diğerlerinin söylediği gibi, sadece biraz farklı bir sözdizimine sahip olan statik değişkenlere erişen sınıf yöntemleri oluşturmak istiyorsunuz.


Düşünülen özellikler bile sözdizimseldir [MyClass sınıfı] yerine MyClass.class gibi şeyler için nokta sözdizimini kullanmak yine de iyi olur.
Zaky Alman

4
@ZakyGerman Yapabilirsin! UIDevice.currentDevice.identifierForVendorbenim için çalışıyor.
tc.

1
@tc. Teşekkür ederim! Şimdi aptalca dolduruyorum. Nedense, bunu geçmişte denediğime ikna oldum ama işe yaramadı. Bu şans eseri yeni bir özellik mi?
Zaky German

1
@ZakyGerman Sınıf yöntemleri için en az bir ya da iki yıl çalıştı. Getter / setter yöntemlerinin beklenen türlere sahip olması durumunda her zaman örnek yöntemler için çalıştığına inanıyorum.
tc.

21

İşte bunu yapmanın güvenli bir yolu:

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

Bu düzenlemeler fooDict öğesinin yalnızca bir kez oluşturulmasını sağlar.

Gönderen Elma belgelerinde : "dispatch_once - Bir blok nesnesi yalnızca bir kez bir uygulamanın ömrü boyunca yürütür."


3
Statik bir NSDictionary + (NSDictionary *) sözlük yönteminin ilk satırında başlatılabildiğinden ve statik olduğundan, yine de yalnızca bir kez başlatılacağı için dispatch_once kodu ilgisiz değil mi?
jcpennypincher

@jcpennypincher onun statik beyan aşağıdaki derleyici hata verir aynı satırda sözlüğü başlatmak için deneniyor: Initializer element is not a compile-time constant.
George WS

@GeorgeWS Bu hatayı yalnızca bir işlevin sonucuna başlatmaya çalıştığınız için (tahsis ve init işlevlerdir). Bunu nil olarak başlatır ve sonra if (obj == nil) ekleyip orada başlatırsanız, iyi olacaksınız.
Rob

1
Rob, bu güvenli değil. Burada sunulan kod en iyisidir.
Ian Ollmann

Bu yanıtı en iyi şekilde seviyorum, çünkü tam ve iş parçacığı güvenli - ancak, op'un sorusu sınıf özelliklerinin beyanı ile ilgili iken - uygulamalarıyla (uygulama ile ilgisi olmayan) sadece uygulama ile daha iyi ilgilenir. Yine de teşekkürler.
Motti Shneor

11

Xcode 8'den itibaren Objective-C artık sınıf özelliklerini destekliyor:

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

Sınıf özellikleri hiçbir zaman sentezlenmediği için kendi uygulamanızı yazmanız gerekir.

@implementation MyClass
static NSUUID*_identifier = nil;

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

Sınıf özelliklerine, sınıf adındaki normal nokta sözdizimini kullanarak erişirsiniz:

MyClass.identifier;

7

Özelliklerin sınıflarda değil, yalnızca nesnelerde değerleri vardır.

Bir sınıfın tüm nesneleri için bir şeyler depolamanız gerekiyorsa, global bir değişken kullanmanız gerekir. staticUygulama dosyasında bildirerek gizleyebilirsiniz .

Ayrıca, nesneleriniz arasında belirli ilişkiler kullanmayı da düşünebilirsiniz: bir master rolünü sınıfınızın belirli bir nesnesiyle ilişkilendirir ve diğer nesneleri bu master'a bağlarsınız. Master sözlüğü basit bir özellik olarak tutacaktır. Kakao uygulamalarında görünüm hiyerarşisinde kullanılan ağaç gibi düşünüyorum.

Başka bir seçenek, hem 'sınıf' sözlüğünüzden hem de bu sözlüğle ilgili tüm nesnelerden oluşan özel bir sınıf nesnesi oluşturmaktır. Bu Kakao'daki gibi bir şey NSAutoreleasePool.


7

Xcode 8'den başlayarak Berbie tarafından yanıtlandığı gibi class özelliği özelliğini kullanabilirsiniz.

Ancak, uygulamada, iVar yerine statik bir değişken kullanarak sınıf özelliği için hem sınıf alıcısını hem de ayarlayıcısını tanımlamanız gerekir.

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

Sınıf düzeyinde çok sayıda özelliğiniz varsa, tek bir kalıp düzeni olabilir. Bunun gibi bir şey:

// Foo.h
@interface Foo

+ (Foo *)singleton;

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

@end

Ve

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

Şimdi bunun gibi sınıf düzeyi özelliklerinize erişin:

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

4
Bu singleton uygulaması iş parçacığı için güvenli değildir, kullanmayın
klefevre

1
Elbette iş parçacığı için güvenli değildir, iş parçacığı güvenliğinden bahseden ilk kişisiniz ve varsayılan olarak iş parçacığı için güvenli olmayan bağlamlarda iş parçacığı güvenliği aşırı yükü, yani tek iş parçacığı anlamsızdır.
Pedro Borges

Burada kullanmak oldukça kolay olurdu dispatch_once.
Ian MacDonald

PO Deklarasyon tarafında uygulama yerine bir cevap istedi - ve önerilen uygulama bile eksik (iş parçacığı için güvenli değil).
Motti Shneor

-3

[Bu çözümü basit deneyin] Bir Swift sınıfında statik bir değişken oluşturabilir ve bunu herhangi bir Objective-C sınıfından çağırabilirsiniz.


1
OP, Swift'te statik bir mülkün nasıl oluşturulacağını sormadı, bu onun sorununu çözmez.
Nathan F.

Ya da daha doğrusu, sorunu çözdü, ancak soruyu cevaplamadı. Hak edip oy vermediğinden emin değilim ...
AmitaiB
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.