Bagaimana cara mendeklarasikan properti tingkat kelas di Objective-C?

205

Mungkin ini sudah jelas, tapi saya tidak tahu bagaimana mendeklarasikan properti kelas di Objective-C.

Saya perlu cache kamus per kelas dan bertanya-tanya bagaimana memasukkannya ke dalam kelas.

mamcx
sumber

Jawaban:

190

properti memiliki arti tertentu dalam Objective-C, tapi saya pikir Anda berarti sesuatu yang setara dengan variabel statis? Misalnya hanya satu contoh untuk semua jenis Foo?

Untuk mendeklarasikan fungsi kelas di Objective-C Anda menggunakan awalan + alih-alih - sehingga implementasi Anda akan terlihat seperti:

// Foo.h
@interface Foo {
}

+ (NSDictionary *)dictionary;

// Foo.m
+ (NSDictionary *)dictionary {
  static NSDictionary *fooDict = nil;
  if (fooDict == nil) {
    // create dict
  }
  return fooDict;
}
Andrew Grant
sumber
23
Apakah ini benar? Tidakkah mengatur fooDict menjadi nol sebagai baris pertama dari metode kamus selalu menghasilkan kamus yang dibuat ulang setiap kali?
PapillonUK
59
Baris statis NSDictionary * fooDict = nil; hanya akan dieksekusi sekali! Bahkan dalam metode yang disebut beberapa kali, deklarasi (dan juga dalam contoh ini inisialisasi) dengan kata kunci statis akan diabaikan jika ada variabel statis dengan nama ini.
Binarian
3
@ BenC.R.Leggiero Ya, tentu saja. The .sintaks -accessor tidak terikat properti di Objective-C, itu hanya disusun-in pintas untuk metode apapun yang kembali sesuatu tanpa mengambil args apapun. Dalam hal ini saya lebih suka - saya pribadi lebih suka .sintaksis untuk setiap penggunaan di mana kode klien bermaksud untuk mendapatkan sesuatu, tidak melakukan tindakan (bahkan jika kode implementasi dapat membuat sesuatu sekali, atau melakukan tindakan efek samping) . .Sintaks penggunaan yang berat juga menghasilkan kode yang lebih mudah dibaca: keberadaan […]s berarti sesuatu yang signifikan sedang dilakukan ketika mengambil .alih menggunakan sintaks.
Slipp D. Thompson
4
Lihatlah jawaban Alex Nolasco, properti kelas tersedia sejak rilis Xcode 8: stackoverflow.com/a/37849467/6666611
n3wbie
113

Saya menggunakan solusi ini:

@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

Dan saya menemukannya sangat berguna sebagai pengganti pola Singleton.

Untuk menggunakannya, cukup akses data Anda dengan notasi titik:

Model.value = 1;
NSLog(@"%d = value", Model.value);
spooki
sumber
3
Itu sangat keren. Tapi apa sih selfartinya di dalam metode kelas?
Todd Lehman
8
@ToddLehman selfadalah objek yang menerima pesan . Dan karena kelas juga objek , dalam hal ini selfberartiModel
spooki
6
Mengapa rajin harus rajin @synchronized?
Matt Kantor
1
Ini sebenarnya cukup keren bahwa ini berfungsi. Bahwa Anda dapat membangunkan properti tingkat kelas Anda sendiri yang bekerja persis seperti aslinya. Saya kira menyinkronkan diri adalah setara dengan menggunakan 'atom' dalam deklarasi properti Anda dan dapat dihilangkan jika Anda menginginkan versi 'nonatomik'? Juga saya akan mempertimbangkan penamaan variabel dukungan dengan '_' sebagai default oleh apple dan juga meningkatkan keterbacaan sejak kembali / pengaturan self.value dari pengambil / penyetel menyebabkan rekursi tak terbatas. Menurut saya ini jawaban yang tepat.
Peter Segerblom
3
Ini keren, tapi ... 10 baris kode hanya untuk membuat 1 anggota statis? apa yang hack. Apple seharusnya membuat fitur ini.
John Henckel
92

Seperti yang terlihat di WWDC 2016 / XCode 8 ( apa yang baru dalam sesi LLVM @ 5: 05). Properti kelas dapat dideklarasikan sebagai berikut

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

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

Perhatikan bahwa properti kelas tidak pernah disintesis

@implementation
static NSString * _someString;
+ (NSString *)someString { return _someString; }
+ (void)setSomeString:(NSString *)newString { _someString = newString; }
@end
Alex Nolasco
sumber
8
Mungkin harus dibuat eksplisit bahwa ini hanya gula untuk deklarasi accessor. Seperti yang Anda catat, properti tidak disintesis: staticvariabel ( ) masih harus dideklarasikan dan digunakan, dan metode diimplementasikan secara eksplisit, seperti sebelumnya. Sintaks dot sudah bekerja sebelumnya juga. Secara keseluruhan, ini terdengar seperti kesepakatan yang lebih besar dari yang sebenarnya.
jscs
6
Masalahnya adalah ini berarti bahwa Anda dapat mengakses lajang dari kode Swift tanpa menggunakan (), dan tipe suffix dihapus oleh konvensi. Misalnya XYZMyClass.shared (Swift 3) alih-alih XYZMyClass.sharedMyClass ()
Ryan
Ini tidak terlihat aman. Jika ini dapat dimutasi dari utas berbeda dalam kode Anda, saya akan memastikan Anda menangani kondisi lomba potensial yang akan dibuat.
smileBot
Antarmuka bersih yang bagus untuk mengkonsumsi kelas tetapi masih banyak pekerjaan. Mencoba ini dengan blok statis dan itu tidak menyenangkan. Sebenarnya hanya menggunakan statis itu jauh lebih mudah.
Departamento B
1
implementasi dapat dengan mudah ditingkatkan untuk perilaku "singleton" yang aman, menggunakan token dispatch_once - tetapi jika tidak, solusinya dengan benar menunjukkan jawabannya
Motti Shneor
63

Jika Anda mencari yang setara dengan tingkat kelas @property, maka jawabannya adalah "tidak ada yang seperti itu". Tapi ingat, @propertyitu hanya gula sintaksis; itu hanya menciptakan metode objek bernama tepat.

Anda ingin membuat metode kelas yang mengakses variabel statis yang, seperti orang lain katakan, hanya memiliki sintaks yang sedikit berbeda.

Jim Puls
sumber
Bahkan properti yang dipikirkan adalah sintaksis. Akan lebih baik jika dapat menggunakan sintaks dot untuk hal-hal seperti MyClass.class alih-alih [kelas MyClass].
Zaky German
4
@ Zakakerman Anda bisa! UIDevice.currentDevice.identifierForVendorbekerja untukku.
tc.
1
@tc. Terima kasih! Mengisi konyol sekarang. Untuk beberapa alasan, saya yakin bahwa saya pernah mencobanya di masa lalu tetapi tidak berhasil. Apakah itu fitur baru?
Zaky German
1
@ Zakakerman Ini telah bekerja selama setidaknya satu atau dua tahun untuk metode kelas. Saya percaya itu selalu bekerja untuk metode contoh jika metode pengambil / penyetel memiliki tipe yang diharapkan.
tc.
21

Inilah cara aman untuk melakukannya:

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

Pengeditan ini memastikan bahwa fooDict hanya dibuat sekali.

Dari dokumentasi Apple : "dispatch_once - Menjalankan objek blok sekali dan hanya sekali seumur hidup aplikasi."

Quentin
sumber
3
Bukankah kode dispatch_once tidak relevan karena NSDictionary statis dapat diinisialisasi pada baris pertama + (NSDictionary *) metode kamus dan karena itu statis hanya akan diinisialisasi sekali saja?
jcpennypincher
@jcpennypincher Mencoba untuk menginisialisasi kamus pada baris yang sama seperti deklarasi statis menghasilkan kesalahan kompilator berikut: Initializer element is not a compile-time constant.
George WS
@ GeorgeWS Anda hanya mendapatkan kesalahan itu karena Anda mencoba untuk menginisialisasi ke hasil fungsi (alokasikan dan init adalah fungsi). Jika Anda menginisialisasi ke nol, dan kemudian menambahkan if (obj == nil) dan menginisialisasi di sana, Anda akan baik-baik saja.
Rob
1
Rob, itu bukan thread yang aman. Kode yang disajikan di sini adalah yang terbaik.
Ian Ollmann
Saya paling suka jawaban ini, karena sudah lengkap dan aman untuk thread - namun, ini hanya menangani implementasi dengan lebih baik, sementara pertanyaan op adalah tentang deklarasi properti kelas - bukan implementasi mereka (yang tidak ada hubungannya dengan implementasi). Bagaimanapun, terima kasih.
Motti Shneor
11

Pada Xcode 8 Objective-C sekarang mendukung properti kelas:

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

Karena properti kelas tidak pernah disintesis, Anda perlu menulis implementasi Anda sendiri.

@implementation MyClass
static NSUUID*_identifier = nil;

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

Anda mengakses properti kelas menggunakan sintaks dot normal pada nama kelas:

MyClass.identifier;
berbie
sumber
7

Properti hanya memiliki nilai dalam objek, bukan kelas.

Jika Anda perlu menyimpan sesuatu untuk semua objek kelas, Anda harus menggunakan variabel global. Anda dapat menyembunyikannya dengan mendeklarasikannya staticdi file implementasi.

Anda juga dapat mempertimbangkan menggunakan hubungan spesifik antara objek Anda: Anda mengaitkan peran master ke objek tertentu dari kelas Anda dan menautkan objek lain ke master ini. Master akan memegang kamus sebagai properti sederhana. Saya berpikir tentang pohon seperti yang digunakan untuk hierarki tampilan dalam aplikasi Kakao.

Pilihan lain adalah membuat objek dari kelas khusus yang terdiri dari kamus 'kelas' Anda dan satu set semua objek yang terkait dengan kamus ini. Ini seperti NSAutoreleasePooldi Cocoa.

mouviciel
sumber
7

Mulai dari Xcode 8, Anda dapat menggunakan atribut properti kelas seperti dijawab oleh Berbie.

Namun, dalam implementasi, Anda perlu mendefinisikan pengambil kelas dan penyetel untuk properti kelas menggunakan variabel statis sebagai pengganti iVar.

Sampel

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

Contoh.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.
sumber
2

Jika Anda memiliki banyak properti tingkat kelas maka pola singleton mungkin dalam urutan. Sesuatu seperti ini:

// Foo.h
@interface Foo

+ (Foo *)singleton;

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

@end

Dan

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

Sekarang akses properti tingkat kelas Anda seperti ini:

[Foo singleton].property1 = value;
value = [Foo singleton].property2;
Pedro Borges
sumber
4
Implementasi tunggal ini tidak aman untuk thread, jangan gunakan
klefevre
1
Tentu saja itu bukan utas aman, Anda adalah orang pertama yang menyebutkan keselamatan utas dan secara default tidak masuk akal keselamatan berlebih utas dalam konteks aman bukan utas yaitu utas tunggal.
Pedro Borges
Ini akan sangat mudah digunakan di dispatch_oncesini.
Ian MacDonald
PO menginginkan jawaban di sisi Deklarasi, bukan implementasi - dan bahkan implementasi yang disarankan tidak lengkap (tidak aman untuk thread).
Motti Shneor
-3

[Coba solusi ini sederhana] Anda dapat membuat variabel statis di kelas Swift kemudian memanggilnya dari kelas Objective-C.

Jawad
sumber
1
OP tidak bertanya bagaimana cara membuat properti statis di Swift, ini tidak menyelesaikan masalahnya.
Nathan F.
Atau lebih tepatnya, itu memecahkan masalah, tetapi tidak menjawab pertanyaan. Tidak yakin apakah itu layak dan memilih ...
AmitaiB