Apa yang Anda miliki baik-baik saja, meskipun Anda bisa memindahkan deklarasi variabel global ke metode + instance Anda (satu-satunya tempat yang perlu digunakan, kecuali Anda mengizinkannya diatur juga) dan menggunakan nama seperti + defaultMyClass atau + sharedMyClass untuk metode Anda. + instance tidak mengungkapkan maksud.
Chris Hanson
Karena tidak mungkin 'jawaban' untuk pertanyaan ini akan berubah dalam waktu dekat, saya menempatkan kunci historis pada pertanyaan tersebut. Dua alasan 1) Banyak tampilan, suara, dan konten yang bagus 2) Untuk mencegah Anda membuka / menutup. Itu adalah pertanyaan yang bagus untuk waktunya, tetapi pertanyaan-pertanyaan semacam ini tidak sesuai untuk Stack Overflow. Kami sekarang memiliki Tinjauan Kode untuk memeriksa kode kerja. Silakan bawa semua diskusi tentang pertanyaan ini ke pertanyaan meta ini .
George Stocker
Jawaban:
207
Pilihan lain adalah menggunakan +(void)initializemetode ini. Dari dokumentasi:
Runtime mengirimkan initializeke setiap kelas dalam suatu program tepat satu kali tepat sebelum kelas, atau kelas apa pun yang mewarisi darinya, dikirim pesan pertama dari dalam program. (Dengan demikian metode ini mungkin tidak pernah dipanggil jika kelas tidak digunakan.) Runtime mengirimkan initializepesan ke kelas dengan cara yang aman. Superclasses menerima pesan ini sebelum subclass mereka.
Jadi Anda bisa melakukan sesuatu yang mirip dengan ini:
Jika runtime hanya akan memanggil ini sekali, apa yang dilakukan BOOL? Apakah itu tindakan pencegahan jika seseorang memanggil fungsi ini secara eksplisit dari kode mereka?
Aftermathew
5
Ya, ini adalah tindakan pencegahan karena fungsinya juga dapat dipanggil langsung.
Robbie Hanson
33
Ini juga diperlukan karena mungkin ada subkelas. Jika mereka tidak mengabaikan +initializeimplementasi superclasses mereka akan dipanggil jika subclass pertama kali digunakan.
Sven
3
@ Paul Anda dapat mengganti releasemetode dan membuatnya kosong. :)
4
@aryaxt: Dari dokumen yang tercantum, ini sudah aman untuk thread. Jadi, panggilan itu sekali per runtime - periode. Ini tampaknya menjadi solusi yang benar, aman, dan efisien secara optimal.
Ini semua yang biasanya harus Anda gunakan untuk lajang. Antara lain, menjaga kelas Anda terpisah instantiable membuat mereka lebih mudah untuk diuji, karena Anda dapat menguji instance terpisah daripada memiliki cara untuk mengatur ulang keadaan mereka.
Chris Hanson
3
Stig Brautaset: Tidak, tidak boleh meninggalkan @sinkronisasi dalam contoh ini. Itu ada untuk menangani kemungkinan kondisi perlombaan dari dua utas yang menjalankan fungsi statis ini pada saat yang sama, keduanya melewati uji "if (! SharedSingleton)" pada saat yang sama, dan dengan demikian menghasilkan dua [alokasi MySingleton]. .. @synchronized {scope block} memaksa utas kedua hipotetis untuk menunggu utas pertama keluar dari {scope block} sebelum diizinkan melanjutkan ke dalamnya. Saya harap ini membantu! =)
MechEthan
3
Apa yang menghentikan seseorang untuk tetap membuat instance objeknya sendiri? MySingleton *s = [[MySingelton alloc] init];
lindon fox
1
@ lindonfox Apa jawaban untuk pertanyaan Anda?
Raffi Khatchadourian
1
@Raffi - maaf saya pikir saya pasti lupa menempelkan jawaban saya. Ngomong-ngomong, saya mendapatkan buku Pro Objective-C Design Patterns for iOSitu dan menjelaskan bagaimana Anda membuat singelton "ketat". Pada dasarnya karena Anda tidak dapat membuat metode inisiasi pribadi, Anda perlu mengganti metode alokasi dan salin. Jadi, jika Anda mencoba dan melakukan sesuatu seperti [[MySingelton alloc] init]Anda akan mendapatkan error run time (meskipun bukan kesalahan waktu kompilasi). Saya tidak mengerti bagaimana semua detail dari penciptaan objek, tetapi Anda menerapkan + (id) allocWithZone:(NSZone *)zoneyang disebut insharedSingleton
lindon fox
59
Per jawaban saya yang lain di bawah ini, saya pikir Anda harus melakukan:
Jangan repot-repot dengan semua yang Anda lakukan di atas. Jadikan (semoga sangat sedikit) lajang Anda secara terpisah-instantiable, dan cukup gunakan metode berbagi / default. Apa yang telah Anda lakukan hanya diperlukan jika Anda benar-benar, HANYA menginginkan satu contoh kelas Anda. Yang tidak Anda lakukan, khususnya. untuk pengujian unit.
Chris Hanson
Masalahnya adalah ini adalah kode sampel Apple untuk "menciptakan singleton". Tapi ya, Anda memang benar.
Colin Barrett
1
Kode sampel Apple benar jika Anda menginginkan singleton "benar" (yaitu objek yang hanya dapat dipakai satu kali, selamanya) tetapi seperti yang dikatakan Chris, ini jarang yang Anda inginkan atau butuhkan sedangkan beberapa jenis contoh bersama yang dapat diatur adalah yang Anda biasanya mau.
Luke Redpath
Berikut ini adalah makro untuk metode di atas: gist.github.com/1057420 . Inilah yang saya gunakan.
Kobski
1
Selain unit test, tidak ada yang menentang solusi ini, benar? Dan itu cepat dan aman.
LearnCocos2D
58
Karena Kendall memposting threadsafe singleton yang berusaha untuk menghindari biaya penguncian, saya pikir saya akan melemparkannya juga:
Kasus cepat: Dalam eksekusi normal sharedInstancetelah ditetapkan, sehingga whileloop tidak pernah dieksekusi dan fungsi kembali setelah hanya menguji keberadaan variabel;
Kasing lambat: Jika sharedInstancetidak ada, maka sebuah instance dialokasikan dan disalin ke dalamnya menggunakan Compare And Swap ('CAS');
Contended case: Jika dua utas keduanya mencoba menelepon sharedInstancepada waktu yang sama DANsharedInstance tidak ada pada saat yang sama, maka keduanya akan menginisialisasi instance baru dari singleton dan mencoba untuk CAS ke posisi. Yang mana saja yang memenangkan CAS, segera kembali, yang mana yang kalah akan melepaskan instance yang baru saja dialokasikan dan mengembalikan (yang sudah ditetapkan) sharedInstance. Single ini OSAtomicCompareAndSwapPtrBarrierbertindak sebagai penghalang tulis untuk utas pengaturan dan pembatas baca dari utas pengujian.
Ini adalah kerja keras yang lengkap untuk paling banyak satu kali itu bisa terjadi selama masa aplikasi. Namun demikian, ini tepat, dan teknik bandingkan-dan-tukar adalah alat yang berguna untuk diketahui, jadi +1.
Steve Madsen
Jawaban yang bagus - keluarga OSAtomic adalah hal yang baik untuk diketahui
Bill
1
@ Louis: Luar biasa, jawaban yang benar-benar mencerahkan! Namun satu pertanyaan: apa yang harus saya initlakukan dengan metode saya dalam pendekatan Anda? Melontar pengecualian saat sharedInstancediinisialisasi bukan ide yang baik, saya percaya. Apa yang harus dilakukan untuk mencegah panggilan pengguna initsecara langsung berkali-kali?
matm
2
Saya biasanya tidak mencegahnya. Sering ada alasan yang sah untuk memungkinkan apa yang umumnya tunggal untuk berlipat ganda, yang paling umum adalah untuk jenis pengujian unit tertentu. Jika saya benar-benar ingin menegakkan satu contoh saya mungkin akan memiliki metode init memeriksa untuk melihat apakah ada global, dan jika memang ada saya memilikinya melepaskan diri dan mengembalikan global.
Saya perhatikan bahwa dentang mengeluh tentang kebocoran jika Anda tidak menetapkan hasil [[self alloc] init]untuk sharedInst.
Pix0r
Mengubur init seperti ini adalah pendekatan IMO yang cukup jelek. Jangan main-main dengan init dan / atau pembuatan objek yang sebenarnya. Jika Anda memilih titik terkontrol akses ke instance bersama, sementara tidak menyulitkan singleton ke objek, Anda akan memiliki waktu yang lebih bahagia nanti jika menulis tes dll. Singleton keras terlalu banyak digunakan.
Dokumentasi Apple merekomendasikan Anda memeriksa jenis kelas di blok inisialisasi Anda. Karena subclass memanggil inisialisasi secara default. Ada kasus yang tidak jelas di mana subclass dapat dibuat secara tidak langsung melalui KVO. Karena jika Anda menambahkan baris berikut di kelas lain:
Saya memiliki variasi yang menarik tentang sharedInstance yang aman, tetapi tidak mengunci setelah inisialisasi. Saya belum cukup yakin untuk memodifikasi jawaban atas seperti yang diminta, tetapi saya menyajikannya untuk diskusi lebih lanjut:
// Volatile to make sure we are not foiled by CPU cachesstaticvolatileALBackendRequestManager*sharedInstance;// There's no need to call this directly, as method swizzling in sharedInstance// means this will get called after the singleton is initialized.+(MySingleton*)simpleSharedInstance
{return(MySingleton*)sharedInstance;}+(MySingleton*)sharedInstance
{@synchronized(self){if(sharedInstance == nil){
sharedInstance =[[MySingleton alloc] init];// Replace expensive thread-safe method // with the simpler one that just returns the allocated instance.
SEL origSel =@selector(sharedInstance);
SEL newSel =@selector(simpleSharedInstance);Method origMethod = class_getClassMethod(self, origSel);Method newMethod = class_getClassMethod(self, newSel);
method_exchangeImplementations(origMethod, newMethod);}}return(MySingleton*)sharedInstance;}
+1 benar-benar menarik. Saya mungkin menggunakan class_replaceMethoduntuk berubah sharedInstancemenjadi tiruan dari simpleSharedInstance. Dengan begitu Anda tidak perlu khawatir tentang mendapatkan @synchronizedkunci lagi.
Dave DeLong
Efeknya sama, menggunakan exchangeImplementations berarti bahwa setelah init ketika Anda memanggil sharedInstance, Anda benar-benar memanggil simpleSharedInstance. Saya benar-benar mulai dengan replaceMethod, tetapi memutuskan lebih baik untuk hanya beralih implementasi sehingga yang asli masih ada jika diperlukan ...
Kendall Helmstetter Gelner
Dalam pengujian lebih lanjut, saya tidak bisa mendapatkan replaceMethod untuk bekerja - dalam panggilan berulang, kode masih memanggil sharedInstance asli, bukan simpleSharedInstance. Saya pikir itu mungkin karena mereka berdua metode tingkat kelas ... Pengganti yang saya gunakan adalah: class_replaceMethod (self, origSel, method_getImplementation (newMethod), method_getTypeEncoding (newMethod)); dan beberapa variasi daripadanya. Saya dapat memverifikasi kode yang saya posting berhasil dan simpleSharedInstance dipanggil setelah melewati sharedInstance pertama.
Kendall Helmstetter Gelner
Anda dapat membuat versi aman utas yang tidak membayar biaya penguncian setelah inisialisasi tanpa melakukan banyak penghancuran runtime, saya telah memposting implementasi di bawah ini.
Louis Gerbarg
1
+1 ide bagus. Saya suka hal-hal yang bisa dilakukan dengan runtime. Tetapi dalam kebanyakan kasus ini mungkin adalah optimasi prematur. Jika saya benar-benar harus menyingkirkan biaya sinkronisasi saya mungkin akan menggunakan versi tanpa kunci oleh Louis.
Pastikan untuk membaca tajuk pengiriman / once.h untuk memahami apa yang terjadi. Dalam hal ini komentar header lebih berlaku daripada halaman dokumen atau manual.
Satu-satunya batasan tentang kelas Singleton, adalah bahwa itu adalah subclass NSObject. Tetapi sebagian besar waktu saya menggunakan lajang dalam kode saya mereka sebenarnya subclass NSObject, sehingga kelas ini benar-benar memudahkan hidup saya dan membuat kode lebih bersih.
Saya menggunakan NSSingleton Anda untuk proyek saya, dan tampaknya tidak kompatibel dengan KVO. Masalahnya adalah bahwa KVO membuat subkelas untuk setiap objek KVO dengan awalan NSKVONotifying_ MyClass . Dan itu membuat MyClass + menginisialisasi dan metode -init dipanggil dua kali.
Oleg Trakhman
Saya menguji ini pada Xcode terbaru dan tidak mengalami kesulitan mendaftar atau menerima acara KVO. Anda dapat memverifikasi ini dengan kode berikut: gist.github.com/3065038 Seperti yang saya sebutkan di Twitter, metode + inisialisasi dipanggil sekali untuk NSSingleton dan sekali untuk setiap subkelas. Ini adalah properti dari Objective-C.
kevinlawler
Jika Anda menambahkan NSLog(@"initialize: %@", NSStringFromClass([self class]));ke +initializemetode Anda dapat memverifikasi bahwa kelas diinisialisasi hanya sekali.
Anda tidak ingin menyinkronkan diri ... Karena objek diri belum ada! Anda akhirnya mengunci nilai id sementara. Anda ingin memastikan bahwa tidak ada orang lain yang dapat menjalankan metode kelas (sharedInstance, alokasi, alokasiWithZone :, dll), jadi Anda perlu menyinkronkan objek kelas:
@implementationMYSingletonstaticMYSingleton* sharedInstance = nil;+( id )sharedInstance {@synchronized([MYSingletonclass]){if( sharedInstance == nil )
sharedInstance =[[MYSingleton alloc ] init ];}return sharedInstance;}+( id )allocWithZone:(NSZone*)zone {@synchronized([MYSingletonclass]){if( sharedInstance == nil )
sharedInstance =[ super allocWithZone:zone ];}return sharedInstance;}-( id )init {@synchronized([MYSingletonclass]){
self =[ super init ];if( self != nil ){// Insert initialization code here}return self;}}@end
Sisa dari metode, metode accessor, metode mutator, dll harus disinkronkan pada diri sendiri. Semua metode kelas dan inisialisasi (dan mungkin -dealloc) harus disinkronkan pada objek kelas. Anda dapat menghindari sinkronisasi secara manual jika Anda menggunakan properti Objective-C 2.0 alih-alih metode accessor / mutator. Semua object.property dan object.property = foo, secara otomatis disinkronkan ke diri.
Rob Dotson
3
Tolong jelaskan mengapa Anda berpikir bahwa selfobjek tidak ada dalam metode kelas. Runtime menentukan implementasi metode mana yang akan dipanggil berdasarkan nilai yang sama persis yang disediakan selfuntuk setiap metode (kelas atau contoh).
dreamlax
2
Di dalam metode kelas, selfadalah objek kelas. Coba sendiri:#import <Foundation/Foundation.h> @interface Eggbert : NSObject + (BOOL) selfIsClassObject; @end @implementation Eggbert + (BOOL) selfIsClassObject { return self == [Eggbert class]; } @end int main (int argc, const char * argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; NSLog(@"%@", [Eggbert selfIsClassObject] ? @"YES" : @"NO"); [pool drain]; return 0; }
jscs
0
Hanya ingin meninggalkan ini di sini jadi saya tidak kehilangan itu. Keuntungan yang satu ini adalah dapat digunakan di InterfaceBuilder, yang merupakan keuntungan BESAR. Ini diambil dari pertanyaan lain yang saya ajukan :
Saya tahu ada banyak komentar tentang "pertanyaan" ini, tetapi saya tidak melihat banyak orang menyarankan menggunakan makro untuk mendefinisikan singleton. Ini adalah pola umum dan makro sangat menyederhanakan singleton.
Berikut adalah makro yang saya tulis berdasarkan beberapa implementasi Objc yang pernah saya lihat.
Singeton.h
/**
@abstract Helps define the interface of a singleton.
@param TYPE The type of this singleton.
@param NAME The name of the singleton accessor. Must match the name used in the implementation.
@discussion
Typcially the NAME is something like 'sharedThing' where 'Thing' is the prefix-removed type name of the class.
*/#defineSingletonInterface(TYPE, NAME) \
+(TYPE *)NAME;/**
@abstract Helps define the implementation of a singleton.
@param TYPE The type of this singleton.
@param NAME The name of the singleton accessor. Must match the name used in the interface.
@discussion
Typcially the NAME is something like 'sharedThing' where 'Thing' is the prefix-removed type name of the class.
*/#defineSingletonImplementation(TYPE, NAME) \
static TYPE *__ ## NAME; \
\
\
+(void)initialize \
{ \
static BOOL initialized = NO; \
if(!initialized) \
{ \
initialized = YES; \
__ ## NAME = [[TYPE alloc] init]; \} \
} \
\
\
+(TYPE *)NAME \
{ \
return __ ## NAME; \}
Mengapa antarmuka makro saat hampir kosong? Konsistensi kode antara file header dan kode; rawatan jika Anda ingin menambahkan lebih banyak metode otomatis atau mengubahnya.
Saya menggunakan metode inisialisasi untuk membuat singleton seperti yang digunakan dalam jawaban paling populer di sini (saat penulisan).
Dengan metode kelas Objective C, kita bisa menghindari menggunakan pola singleton dengan cara biasa, dari:
[[Librarian sharedInstance] openLibrary]
untuk:
[Librarian openLibrary]
dengan membungkus kelas di dalam kelas lain yang hanya memiliki Metode Kelas , dengan cara itu tidak ada kesempatan untuk membuat instance duplikat, karena kita tidak membuat instance apa pun!
Jika singleton sudah diinisialisasi, blok LOCK tidak akan dimasukkan. Pemeriksaan kedua jika (! Diinisialisasi) adalah untuk memastikan belum diinisialisasi ketika utas saat ini memperoleh KUNCI.
Saya biasanya menggunakan kode yang kira-kira mirip dengan jawaban Ben Hoffstein (yang juga saya dapatkan dari Wikipedia). Saya menggunakannya untuk alasan yang dinyatakan oleh Chris Hanson dalam komentarnya.
Namun, kadang-kadang saya memiliki kebutuhan untuk menempatkan singleton ke NIB, dan dalam hal ini saya menggunakan yang berikut:
Saya menyerahkan implementasi -retain(dll) kepada pembaca, meskipun kode di atas adalah semua yang Anda butuhkan di lingkungan sampah yang dikumpulkan.
Kode Anda tidak aman untuk thread. Ini menggunakan disinkronkan dalam metode alokasi, tetapi tidak dalam metode init. Memeriksa bool yang diinisialisasi tidak aman untuk thread.
Mecki
-5
Jawaban yang diterima, meskipun dikompilasi, salah.
+(MySingleton*)sharedInstance
{@synchronized(self)<-------- self does not exist at class scope
{if(sharedInstance == nil)
sharedInstance =[[MySingleton alloc] init];}return sharedInstance;}
Per dokumentasi Apple:
... Anda dapat mengambil pendekatan serupa untuk menyinkronkan metode kelas dari kelas terkait, menggunakan objek Kelas bukan diri.
Bahkan jika menggunakan karya sendiri, seharusnya tidak dan ini terlihat seperti kesalahan salin dan tempel ke saya. Implementasi yang benar untuk metode pabrik kelas adalah:
diri tentu saja ada ruang lingkup kelasnya. Itu merujuk ke kelas bukan contoh kelas. Kelas adalah (kebanyakan) objek kelas satu.
schwa
Mengapa Anda memasukkan @sinkronkan dalam suatu metode?
user4951
1
Seperti yang sudah dikatakan schwa, selfadalah objek kelas di dalam metode kelas. Lihat komentar saya untuk cuplikan yang menunjukkan ini.
jscs
selfada, tetapi menggunakannya sebagai pengenal yang diteruskan ke @synchronizedakan menyinkronkan akses ke metode instance. Sebagai @ user490696 tunjukkan, ada kasus (seperti lajang) di mana menggunakan objek kelas lebih disukai. Dari Panduan Pemrograman Obj-C:You can take a similar approach to synchronize the class methods of the associated class, using the class object instead of self. In the latter case, of course, only one thread at a time is allowed to execute a class method because there is only one class object that is shared by all callers.
Jawaban:
Pilihan lain adalah menggunakan
+(void)initialize
metode ini. Dari dokumentasi:Jadi Anda bisa melakukan sesuatu yang mirip dengan ini:
sumber
+initialize
implementasi superclasses mereka akan dipanggil jika subclass pertama kali digunakan.release
metode dan membuatnya kosong. :)[Sumber]
sumber
MySingleton *s = [[MySingelton alloc] init];
Pro Objective-C Design Patterns for iOS
itu dan menjelaskan bagaimana Anda membuat singelton "ketat". Pada dasarnya karena Anda tidak dapat membuat metode inisiasi pribadi, Anda perlu mengganti metode alokasi dan salin. Jadi, jika Anda mencoba dan melakukan sesuatu seperti[[MySingelton alloc] init]
Anda akan mendapatkan error run time (meskipun bukan kesalahan waktu kompilasi). Saya tidak mengerti bagaimana semua detail dari penciptaan objek, tetapi Anda menerapkan+ (id) allocWithZone:(NSZone *)zone
yang disebut insharedSingleton
Per jawaban saya yang lain di bawah ini, saya pikir Anda harus melakukan:
sumber
Karena Kendall memposting threadsafe singleton yang berusaha untuk menghindari biaya penguncian, saya pikir saya akan melemparkannya juga:
Oke, izinkan saya menjelaskan cara kerjanya:
Kasus cepat: Dalam eksekusi normal
sharedInstance
telah ditetapkan, sehinggawhile
loop tidak pernah dieksekusi dan fungsi kembali setelah hanya menguji keberadaan variabel;Kasing lambat: Jika
sharedInstance
tidak ada, maka sebuah instance dialokasikan dan disalin ke dalamnya menggunakan Compare And Swap ('CAS');Contended case: Jika dua utas keduanya mencoba menelepon
sharedInstance
pada waktu yang sama DANsharedInstance
tidak ada pada saat yang sama, maka keduanya akan menginisialisasi instance baru dari singleton dan mencoba untuk CAS ke posisi. Yang mana saja yang memenangkan CAS, segera kembali, yang mana yang kalah akan melepaskan instance yang baru saja dialokasikan dan mengembalikan (yang sudah ditetapkan)sharedInstance
. Single iniOSAtomicCompareAndSwapPtrBarrier
bertindak sebagai penghalang tulis untuk utas pengaturan dan pembatas baca dari utas pengujian.sumber
init
lakukan dengan metode saya dalam pendekatan Anda? Melontar pengecualian saatsharedInstance
diinisialisasi bukan ide yang baik, saya percaya. Apa yang harus dilakukan untuk mencegah panggilan penggunainit
secara langsung berkali-kali?sumber
[[self alloc] init]
untuk sharedInst.Sunting: Implementasi ini usang dengan ARC. Silakan lihat Bagaimana cara mengimplementasikan singleton Objective-C yang kompatibel dengan ARC? untuk implementasi yang benar.
Semua implementasi inisialisasi yang saya baca di jawaban lain berbagi kesalahan umum.
Dokumentasi Apple merekomendasikan Anda memeriksa jenis kelas di blok inisialisasi Anda. Karena subclass memanggil inisialisasi secara default. Ada kasus yang tidak jelas di mana subclass dapat dibuat secara tidak langsung melalui KVO. Karena jika Anda menambahkan baris berikut di kelas lain:
Objective-C secara implisit akan membuat subkelas MySingletonClass yang menghasilkan pemicu kedua
+initialize
.Anda mungkin berpikir bahwa Anda harus secara implisit memeriksa inisialisasi duplikat di blok init Anda seperti itu:
Tapi Anda akan menembak diri sendiri di kaki; atau lebih buruk memberi pengembang lain kesempatan untuk menembak diri mereka sendiri.
TL; DR, ini implementasi saya
(Ganti ZAssert dengan makro pernyataan kami sendiri; atau hanya NSAssert.)
sumber
Penjelasan menyeluruh tentang kode makro Singleton ada di blog Cocoa With Love
http://cocoawithlove.com/2008/11/singletons-appdelegates-and-top-level.html .
sumber
Saya memiliki variasi yang menarik tentang sharedInstance yang aman, tetapi tidak mengunci setelah inisialisasi. Saya belum cukup yakin untuk memodifikasi jawaban atas seperti yang diminta, tetapi saya menyajikannya untuk diskusi lebih lanjut:
sumber
class_replaceMethod
untuk berubahsharedInstance
menjadi tiruan darisimpleSharedInstance
. Dengan begitu Anda tidak perlu khawatir tentang mendapatkan@synchronized
kunci lagi.Jawaban singkat: Luar biasa.
Jawaban panjang: Sesuatu seperti ....
Pastikan untuk membaca tajuk pengiriman / once.h untuk memahami apa yang terjadi. Dalam hal ini komentar header lebih berlaku daripada halaman dokumen atau manual.
sumber
Saya telah memasukkan singleton ke dalam sebuah kelas, sehingga kelas-kelas lain dapat mewarisi sifat-sifat singleton.
Singleton.h:
Singleton.m:
Dan di sini adalah contoh dari beberapa kelas, bahwa Anda ingin menjadi singleton.
Satu-satunya batasan tentang kelas Singleton, adalah bahwa itu adalah subclass NSObject. Tetapi sebagian besar waktu saya menggunakan lajang dalam kode saya mereka sebenarnya subclass NSObject, sehingga kelas ini benar-benar memudahkan hidup saya dan membuat kode lebih bersih.
sumber
@synchronized
sangat lambat dan harus dihindari.Ini juga berfungsi di lingkungan yang dikumpulkan tanpa sampah.
sumber
Tidakkah ini akan menjadi threadsafe dan menghindari penguncian yang mahal setelah panggilan pertama?
sumber
Berikut ini makro yang saya kumpulkan:
http://github.com/cjhanson/Objective-C-Optimized-Singleton
Ini didasarkan pada pekerjaan di sini oleh Matt Gallagher Tetapi mengubah implementasi untuk menggunakan metode swizzling seperti dijelaskan di sini oleh Dave MacLachlan dari Google .
Saya menyambut komentar / kontribusi.
sumber
Bagaimana tentang
Jadi Anda menghindari biaya sinkronisasi setelah inisialisasi?
sumber
Untuk diskusi mendalam tentang pola tunggal di Objective-C, lihat di sini:
Menggunakan Pola Singleton di Objective-C
sumber
KLSingleton
sumber
NSLog(@"initialize: %@", NSStringFromClass([self class]));
ke+initialize
metode Anda dapat memverifikasi bahwa kelas diinisialisasi hanya sekali.Anda tidak ingin menyinkronkan diri ... Karena objek diri belum ada! Anda akhirnya mengunci nilai id sementara. Anda ingin memastikan bahwa tidak ada orang lain yang dapat menjalankan metode kelas (sharedInstance, alokasi, alokasiWithZone :, dll), jadi Anda perlu menyinkronkan objek kelas:
sumber
self
objek tidak ada dalam metode kelas. Runtime menentukan implementasi metode mana yang akan dipanggil berdasarkan nilai yang sama persis yang disediakanself
untuk setiap metode (kelas atau contoh).self
adalah objek kelas. Coba sendiri:#import <Foundation/Foundation.h> @interface Eggbert : NSObject + (BOOL) selfIsClassObject; @end @implementation Eggbert + (BOOL) selfIsClassObject { return self == [Eggbert class]; } @end int main (int argc, const char * argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; NSLog(@"%@", [Eggbert selfIsClassObject] ? @"YES" : @"NO"); [pool drain]; return 0; }
Hanya ingin meninggalkan ini di sini jadi saya tidak kehilangan itu. Keuntungan yang satu ini adalah dapat digunakan di InterfaceBuilder, yang merupakan keuntungan BESAR. Ini diambil dari pertanyaan lain yang saya ajukan :
sumber
sumber
Saya tahu ada banyak komentar tentang "pertanyaan" ini, tetapi saya tidak melihat banyak orang menyarankan menggunakan makro untuk mendefinisikan singleton. Ini adalah pola umum dan makro sangat menyederhanakan singleton.
Berikut adalah makro yang saya tulis berdasarkan beberapa implementasi Objc yang pernah saya lihat.
Singeton.h
Contoh penggunaan:
MyManager.h
MyManager.m
Mengapa antarmuka makro saat hampir kosong? Konsistensi kode antara file header dan kode; rawatan jika Anda ingin menambahkan lebih banyak metode otomatis atau mengubahnya.
Saya menggunakan metode inisialisasi untuk membuat singleton seperti yang digunakan dalam jawaban paling populer di sini (saat penulisan).
sumber
Dengan metode kelas Objective C, kita bisa menghindari menggunakan pola singleton dengan cara biasa, dari:
untuk:
dengan membungkus kelas di dalam kelas lain yang hanya memiliki Metode Kelas , dengan cara itu tidak ada kesempatan untuk membuat instance duplikat, karena kita tidak membuat instance apa pun!
Saya menulis blog yang lebih rinci di sini :)
sumber
Untuk memperluas contoh dari @ robbie-hanson ...
sumber
Cara saya sederhana seperti ini:
Jika singleton sudah diinisialisasi, blok LOCK tidak akan dimasukkan. Pemeriksaan kedua jika (! Diinisialisasi) adalah untuk memastikan belum diinisialisasi ketika utas saat ini memperoleh KUNCI.
sumber
initialized
sebagaivolatile
cukup. Lihat aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf .Saya belum membaca semua solusinya, jadi maafkan jika kode ini berlebihan.
Ini adalah implementasi yang paling aman menurut saya.
sumber
Saya biasanya menggunakan kode yang kira-kira mirip dengan jawaban Ben Hoffstein (yang juga saya dapatkan dari Wikipedia). Saya menggunakannya untuk alasan yang dinyatakan oleh Chris Hanson dalam komentarnya.
Namun, kadang-kadang saya memiliki kebutuhan untuk menempatkan singleton ke NIB, dan dalam hal ini saya menggunakan yang berikut:
Saya menyerahkan implementasi
-retain
(dll) kepada pembaca, meskipun kode di atas adalah semua yang Anda butuhkan di lingkungan sampah yang dikumpulkan.sumber
Jawaban yang diterima, meskipun dikompilasi, salah.
Per dokumentasi Apple:
... Anda dapat mengambil pendekatan serupa untuk menyinkronkan metode kelas dari kelas terkait, menggunakan objek Kelas bukan diri.
Bahkan jika menggunakan karya sendiri, seharusnya tidak dan ini terlihat seperti kesalahan salin dan tempel ke saya. Implementasi yang benar untuk metode pabrik kelas adalah:
sumber
self
adalah objek kelas di dalam metode kelas. Lihat komentar saya untuk cuplikan yang menunjukkan ini.self
ada, tetapi menggunakannya sebagai pengenal yang diteruskan ke@synchronized
akan menyinkronkan akses ke metode instance. Sebagai @ user490696 tunjukkan, ada kasus (seperti lajang) di mana menggunakan objek kelas lebih disukai. Dari Panduan Pemrograman Obj-C:You can take a similar approach to synchronize the class methods of the associated class, using the class object instead of self. In the latter case, of course, only one thread at a time is allowed to execute a class method because there is only one class object that is shared by all callers.