Saya mencoba mencari model tunggal yang sesuai untuk digunakan di Swift. Sejauh ini, saya sudah bisa mendapatkan model aman non-utas bekerja sebagai:
class var sharedInstance: TPScopeManager {
get {
struct Static {
static var instance: TPScopeManager? = nil
}
if !Static.instance {
Static.instance = TPScopeManager()
}
return Static.instance!
}
}
Membungkus instance singleton dalam Static struct harus memungkinkan satu instance yang tidak bertabrakan dengan instance singleton tanpa skema penamaan yang rumit, dan itu harus membuat semuanya menjadi sangat pribadi. Namun jelas, model ini tidak aman untuk thread. Jadi saya mencoba menambahkan dispatch_once
semuanya:
class var sharedInstance: TPScopeManager {
get {
struct Static {
static var instance: TPScopeManager? = nil
static var token: dispatch_once_t = 0
}
dispatch_once(Static.token) { Static.instance = TPScopeManager() }
return Static.instance!
}
}
Tapi saya mendapatkan kesalahan kompiler di dispatch_once
telepon:
Tidak dapat mengonversi jenis ekspresi 'Void' ke mengetik '()'
Saya sudah mencoba beberapa varian sintaks yang berbeda, tetapi semuanya tampaknya memiliki hasil yang sama:
dispatch_once(Static.token, { Static.instance = TPScopeManager() })
Apa penggunaan yang tepat dispatch_once
menggunakan Swift? Saya awalnya berpikir masalahnya adalah dengan blok karena ()
dalam pesan kesalahan, tetapi semakin saya melihatnya, semakin saya pikir itu mungkin masalah untuk mendapatkan dispatch_once_t
definisi yang benar.
@lazy
harus aman.Static.instance = TPScopeManager()
memaksa tipe instance. Jika Anda menggunakan sesuatu sepertiStatic.instance = self()
dengan inisialisasi yang diperlukan, kelas tipe yang sesuai akan dihasilkan. Meski begitu, dan ini adalah hal penting yang perlu diperhatikan, hanya sekali untuk semua instance dalam hierarki! Jenis pertama yang diinisialisasi adalah jenis yang ditetapkan untuk semua instance. Saya tidak berpikir objektif-c berperilaku sama.Jawaban:
tl; dr: Gunakan pendekatan konstanta kelas jika Anda menggunakan Swift 1.2 atau lebih tinggi dan pendekatan struct bersarang jika Anda perlu mendukung versi sebelumnya.
Dari pengalaman saya dengan Swift ada tiga pendekatan untuk menerapkan pola Singleton yang mendukung inisialisasi malas dan keamanan utas.
Konstanta kelas
Pendekatan ini mendukung inisialisasi malas karena Swift malas menginisialisasi konstanta kelas (dan variabel), dan aman menurut definisi
let
. Ini sekarang secara resmi direkomendasikan untuk instantiate singleton.Konstanta kelas diperkenalkan di Swift 1.2. Jika Anda perlu mendukung versi Swift sebelumnya, gunakan pendekatan struct bersarang di bawah ini atau konstanta global.
Bersarang struct
Di sini kita menggunakan konstanta statis dari struct bersarang sebagai konstanta kelas. Ini adalah solusi untuk kurangnya konstanta kelas statis di Swift 1.1 dan sebelumnya, dan masih berfungsi sebagai solusi untuk kurangnya konstanta statis dan variabel dalam fungsi.
dispatch_once
Pendekatan Objective-C tradisional porting ke Swift. Saya cukup yakin tidak ada keuntungan atas pendekatan struct bersarang tetapi saya tetap menempatkannya di sini karena saya menemukan perbedaan dalam sintaksis yang menarik.
Lihat proyek GitHub ini untuk pengujian unit.
sumber
init
juga dinyatakanprivate
untuk menjamin satu dan hanya satu contoh objek yang akan ada sepanjang masa pakai aplikasi?final
agar Anda tidak membuat subkelasnya; dan (b) mendeklarasikaninit
metode tersebutprivate
agar Anda tidak dapat secara tidak sengaja membuat instance lain di suatu tempat.Karena Apple sekarang telah mengklarifikasi bahwa variabel struct statis diinisialisasi baik malas dan dibungkus
dispatch_once
(lihat catatan di akhir posting), saya pikir solusi terakhir saya adalah:Ini mengambil keuntungan dari inisialisasi malas otomatis dari elemen struct statis, dengan aman menyembunyikan implementasi aktual dari konsumen, menjaga semuanya terkotak secara kompak untuk keterbacaan, dan menghilangkan variabel global yang terlihat.
Apple telah mengklarifikasi bahwa inisialisasi malas bersifat aman, jadi tidak perlu
dispatch_once
atau perlindungan serupaDari sini
sumber
private init() {}
:, untuk lebih menegakkan fakta bahwa kelas ini tidak dimaksudkan untuk dipakai secara eksternal.Untuk Swift 1.2 dan lebih tinggi:
Dengan bukti kebenaran (semua kredit ada di sini ), sekarang tidak ada alasan untuk menggunakan metode sebelumnya untuk lajang.
Pembaruan : Sekarang ini adalah cara resmi untuk mendefinisikan lajang sebagaimana dijelaskan dalam dokumen resmi !
Adapun kekhawatiran tentang penggunaan
static
vsclass
.static
harus menjadi orang yang digunakan bahkan ketikaclass
variabel tersedia. Lajang tidak dimaksudkan untuk disubklasifikasi karena itu akan menghasilkan beberapa contoh basis tunggal. Menggunakanstatic
menegakkan ini dengan cara yang indah, Swifty.Untuk Swift 1.0 dan 1.1:
Dengan perubahan baru-baru ini di Swift, sebagian besar metode kontrol akses baru, saya sekarang condong ke arah cara yang lebih bersih menggunakan variabel global untuk lajang.
Seperti yang disebutkan dalam artikel blog Swift di sini :
Cara membuat singleton ini adalah utas yang aman, cepat, malas, dan juga dijembatani ke ObjC secara gratis.
sumber
private init() {}
sebagai inisialisasi dariSingletonClass
. untuk mencegah instantiate dari luar.Swift 1.2 atau yang lebih baru sekarang mendukung variabel / konstanta statis di kelas. Jadi Anda bisa menggunakan konstanta statis:
sumber
Ada cara yang lebih baik untuk melakukannya. Anda bisa mendeklarasikan variabel global di kelas Anda di atas deklarasi kelas seperti ini:
Ini hanya memanggil init default Anda atau variabel init dan global mana pun secara
dispatch_once
default di Swift. Kemudian di kelas mana pun Anda ingin mendapatkan referensi, Anda cukup melakukan ini:Jadi pada dasarnya Anda dapat menyingkirkan seluruh blok kode instance bersama.
sumber
TPScopeManager.sharedInstance.doIt()
sepanjang waktu, cukup beri nama kelas AndaTPScopeManagerClass
, buat deklarasi ini di sebelah kelaspublic let TPScopeManager = TPScopeManagerClass()
, dan saat menggunakan tulis sajaTPScopeManager.doIt()
. Sangat bersih!TPScopeManager
, dan oleh karenanya definisi tersebut bukan singleton .Swift lajang yang terkena dalam kerangka Kakao sebagai fungsi kelas, misalnya
NSFileManager.defaultManager()
,NSNotificationCenter.defaultCenter()
. Jadi lebih masuk akal sebagai fungsi kelas untuk mencerminkan perilaku ini, daripada variabel kelas seperti beberapa solusi lainnya. misalnya:Ambil singleton via
MyClass.sharedInstance()
.sumber
static
properti.Per dokumentasi Apple , telah diulang berkali-kali bahwa cara termudah untuk melakukan ini di Swift adalah dengan properti tipe statis:
Namun, jika Anda mencari cara untuk melakukan pengaturan tambahan di luar panggilan konstruktor sederhana, rahasianya adalah menggunakan penutupan yang segera dipanggil:
Ini dijamin aman-utas dan diinisialisasi dengan malas hanya sekali.
sumber
Swift 4+
sumber
Melihat kode sampel Apple, saya menemukan pola ini. Saya tidak yakin bagaimana Swift berurusan dengan statika, tetapi ini akan aman di C #. Saya menyertakan properti dan metode untuk Objective-C interop.
sumber
dispatch_once
barang. Saya bertaruh pada gaya Anda. :)class
dalam deklarasi kelas sama denganstatic
dalam deklarasi struct?dispatch_once
kemampuan ini .Secara singkat,
Anda mungkin ingin membaca File dan Inisialisasi
sumber
Jika Anda berencana untuk menggunakan kelas Swift singleton Anda di Objective-C, setup ini akan membuat kompiler menghasilkan header Objective-C-like yang sesuai:
Kemudian di kelas Objective-C Anda dapat memanggil singleton Anda seperti yang Anda lakukan di masa pra-Swift:
Ini hanya implementasi sederhana saya.
sumber
NSFileManager.defaultManager()
, tetapi masih menggunakan mekanisme anggota statis Swift yang malas thread-safe.Solusi pertama
Kemudian dalam kode Anda:
Solusi kedua
Dan nanti dalam kode Anda, Anda akan dapat menjaga kawat gigi agar kurang kebingungan:
sumber
Kalau begitu sebut saja;
sumber
init
sebagaiprivate
, tetapi juga untuk membuatsharedMyModel
sepertifinal
! Demi pembaca masa depan, dalam Swift 3, kita mungkin cenderung untuk mengubah namasharedMyModel
menjadi sederhanashared
.Menggunakan:
Cara Penggunaan:
sumber
Pendekatan terbaik di Swift di atas 1.2 adalah singleton satu baris, karena -
Untuk mengetahui lebih detail tentang pendekatan ini, Anda dapat mengunjungi tautan ini .
sumber
NSObject
subkelas? Selain itu, ini tampaknya pada dasarnya sama dengan stackoverflow.com/a/28436202/1187415 .Dari Apple Documents (Swift 3.0.1),
sumber
Saya akan menyarankan
enum
, seperti yang akan Anda gunakan di Jawa, misalnyasumber
Sekedar referensi, berikut ini adalah contoh implementasi Singleton dari implementasi Nested Struct Jack Wu / hpique. Implementasi juga menunjukkan bagaimana pengarsipan dapat bekerja, serta beberapa fungsi yang menyertainya. Saya tidak dapat menemukan contoh lengkap ini, jadi semoga ini membantu seseorang!
Dan jika Anda tidak mengenali beberapa fungsi-fungsi itu, berikut adalah file utilitas Swift kecil yang pernah saya gunakan:
sumber
Dengan cepat, Anda dapat membuat kelas singleton dengan cara berikut:
sumber
Saya lebih suka implementasi ini:
sumber
Cara implementasi saya di Swift ...
ConfigurationManager.swift
Akses globalDic dari layar aplikasi mana saja dengan cara di bawah ini.
Baca:
Menulis:
sumber
Satu-satunya pendekatan yang tepat di bawah ini.
Untuk mengakses
Alasan:
static
tipe properti dijamin akan diinisialisasi malas hanya sekali, bahkan ketika diakses di beberapa utas secara bersamaan, jadi tidak perlu menggunakandispatch_once
init
metode sehingga instance tidak dapat dibuat oleh kelas lain.final
kelas karena Anda tidak ingin kelas lain mewarisi kelas Singleton.sumber
static let sharedInstance = Singleton()
Setelah melihat implementasi David, sepertinya tidak perlu memiliki fungsi kelas tunggal
instanceMethod
karenalet
melakukan hampir sama dengansharedInstance
metode kelas. Yang perlu Anda lakukan adalah mendeklarasikannya sebagai konstanta global dan hanya itu.sumber
sumber
dispatch_once
karena inisialisasi variabel statis malas dan secara otomatis dilindungi melaluidispatch_once
Apple sebenarnya merekomendasikan menggunakan statika daripada dispatch_once karena alasan itu.Cepat untuk menyadari singleton di masa lalu, tidak lebih dari tiga cara: variabel global, variabel internal dan cara dispatch_once.
Berikut adalah dua singleton yang baik. (Catatan: tidak peduli apa jenis tulisan harus memperhatikan metode init () privatisasi. Karena di Swift, semua standar konstruktor objek adalah publik, perlu ditulis ulang init dapat diubah menjadi pribadi , cegah objek lain dari kelas ini '()' dengan metode inisialisasi default untuk membuat objek.)
Metode 1:
Metode 2:
sumber
Ini adalah yang paling sederhana dengan kemampuan aman thread. Tidak ada utas lain yang dapat mengakses objek tunggal yang sama bahkan jika mereka mau. Cepat 3/4
sumber
Saya meminta singleton saya untuk mengizinkan warisan, dan tidak ada solusi yang benar-benar mengizinkannya. Jadi saya datang dengan ini:
Singleton.sharedInstance()
pertama itu akan mengembalikan instance dariSingleton
SubSingleton.sharedInstance()
pertama itu akan mengembalikan instance yangSubSingleton
dibuat.SubSingleton.sharedInstance()
iniSingleton
adalah benar dan contoh yang sama digunakan.Masalah dengan pendekatan kotor pertama ini adalah bahwa saya tidak dapat menjamin bahwa subclass akan mengimplementasikan
dispatch_once_t
dan memastikan bahwasharedInstanceVar
hanya dimodifikasi satu kali per kelas.Saya akan mencoba untuk memperbaiki ini lebih lanjut, tetapi akan menarik untuk melihat apakah ada yang punya perasaan kuat terhadap ini (selain fakta bahwa itu adalah kata kerja dan perlu memperbaruinya secara manual).
sumber
Ini implementasi saya. Itu juga mencegah programmer membuat contoh baru:
sumber
private init
sudah disarankan di sini: stackoverflow.com/a/28436202/1187415 .Saya menggunakan sintaks berikut:
Ini berfungsi dari Swift 1.2 hingga 4, dan memiliki beberapa keunggulan:
Singleton.instance
sumber