Menggunakan model singleton dispatch_once di Swift


575

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_oncesemuanya:

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_oncetelepon:

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_oncemenggunakan 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_tdefinisi yang benar.


3
Saya akan menghapus semua kode statis dan menggunakan properti readonly dengan initializer @ lazy.
Sulthan

1
Itu yang saya maksud. Sayangnya kami masih belum memiliki informasi yang cukup tentang internal. Namun, IMHO implementasi apa pun @lazyharus aman.
Sulthan

1
Dan cara ini juga memiliki keuntungan tidak mengekspos implementasi ke pemangsaan pemanggil.
David Berry

1
Tampaknya juga Anda tidak dapat memiliki variabel kelas @lazy.
David Berry

Hati-hati! Dua hal yang perlu diperhatikan dengan pendekatan ini. Pertama, setiap kelas yang mewarisi dari ini harus mengganti properti sharedInstance. Static.instance = TPScopeManager()memaksa tipe instance. Jika Anda menggunakan sesuatu seperti Static.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.
sean woodward

Jawaban:


713

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

class Singleton  {
   static let sharedInstance = Singleton()
}

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

class Singleton {
    class var sharedInstance: Singleton {
        struct Static {
            static let instance: Singleton = Singleton()
        }
        return Static.instance
    }
}

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.

class Singleton {
    class var sharedInstance: Singleton {
        struct Static {
            static var onceToken: dispatch_once_t = 0
            static var instance: Singleton? = nil
        }
        dispatch_once(&Static.onceToken) {
            Static.instance = Singleton()
        }
        return Static.instance!
    }
}

Lihat proyek GitHub ini untuk pengujian unit.


13
"utas aman berdasarkan izin" - apakah ini sudah dinyatakan di mana saja? Saya tidak dapat menemukan menyebutkannya di dokumentasi.
jtbandes

4
@jtbandes Konstanta aman untuk semua bahasa yang saya tahu.
hpique

2
@ DaveWood Saya menganggap Anda sedang berbicara tentang pendekatan terakhir. Saya akan mengutip diri saya sendiri: "Saya akan mengatakan tidak perlu lagi menggunakan pendekatan ini, tetapi saya tetap menempatkannya di sini karena saya menemukan perbedaan dalam sintaksis yang menarik."
hpique

5
Haruskah initjuga dinyatakan privateuntuk menjamin satu dan hanya satu contoh objek yang akan ada sepanjang masa pakai aplikasi?
Andrew

5
Dalam pendekatan "Kelas konstan", saya sarankan (a) mendeklarasikan kelas finalagar Anda tidak membuat subkelasnya; dan (b) mendeklarasikan initmetode tersebut privateagar Anda tidak dapat secara tidak sengaja membuat instance lain di suatu tempat.
Rob

175

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:

class WithSingleton {
    class var sharedInstance: WithSingleton {
        struct Singleton {
            static let instance = WithSingleton()
        }

        return Singleton.instance
    }
}

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_onceatau perlindungan serupa

Inisialisasi lazy untuk variabel global (juga untuk anggota statis struct dan enums) dijalankan pertama kali global diakses, dan diluncurkan sebagai dispatch_once untuk memastikan bahwa inisialisasi adalah atom. Ini memungkinkan cara keren untuk menggunakan dispatch_once dalam kode Anda: cukup deklarasikan variabel global dengan penginisialisasi dan tandai sebagai pribadi.

Dari sini


1
Untuk mengonfirmasi: variabel global malas, inisialisasi aman thread, tetapi variabel kelas tidak. Baik?
Bill

14
Saya akan menambahkan bahwa praktik yang baik akan menyatakan inisialisasi sebagai pribadi private init() {}:, untuk lebih menegakkan fakta bahwa kelas ini tidak dimaksudkan untuk dipakai secara eksternal.
Pascal Bourque

1
jadi inisialisasi struct var statis adalah malas dan aman utas, bagaimana jika struct var statis itu adalah kamus untuk multitons, maka kita harus secara manual menyinkronkan / mengantri panggilan ke sana untuk setiap akses, kan?

Jika saya memahami pertanyaan Anda dengan benar, akses kamus dan array secara inheren tidak aman, jadi Anda perlu menggunakan beberapa bentuk sinkronisasi utas.
David Berry

@ DavidvidBerry Bagaimana saya harus memanggil fungsi di dalam kelas singleton ini? Saya perlu fungsi untuk dipanggil pada panggilan pertama myClass.shareInstance.
Ameet Dhas

163

Untuk Swift 1.2 dan lebih tinggi:

class Singleton  {
   static let sharedInstance = Singleton()
}

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 staticvs class. staticharus menjadi orang yang digunakan bahkan ketika classvariabel 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.

private let _singletonInstance = SingletonClass()
class SingletonClass {
  class var sharedInstance: SingletonClass {
    return _singletonInstance
  }
}

Seperti yang disebutkan dalam artikel blog Swift di sini :

Inisialisasi lazy untuk variabel global (juga untuk anggota statis struct dan enums) dijalankan pertama kali global diakses, dan diluncurkan sebagai dispatch_once untuk memastikan bahwa inisialisasi adalah atom. Ini memungkinkan cara keren untuk menggunakan dispatch_once dalam kode Anda: cukup deklarasikan variabel global dengan penginisialisasi dan tandai sebagai pribadi.

Cara membuat singleton ini adalah utas yang aman, cepat, malas, dan juga dijembatani ke ObjC secara gratis.


2
Siapa pun yang hanya membaca jawaban ini: Ingatlah untuk membuat token statis, jika tidak maka perilaku tidak akan ditentukan. Lihat pertanyaan yang diedit David untuk kode lengkap.
nschum

@nschum sebaliknya, perilaku tidak terdefinisi, itu hanya rusak dengan cara yang terdefinisi dengan baik: blok akan selalu dijalankan.
Michael

@Michael: Dokumentasi menyatakan itu tidak terdefinisi. Perilaku saat ini karena itu kebetulan.
nschum

1
Itu hal yang aneh untuk dikatakan. Jika dokumentasi menyebutnya "tidak terdefinisi" itu berarti siapa pun yang menulis kode tidak menjanjikan apa pun. Itu tidak ada hubungannya dengan kode mengetahui jika variabel itu statis. Ini hanya berarti bahwa perilaku saat ini (atau yang tampak) tidak dapat diandalkan.
nschum

6
Anda mungkin ingin menambahkan private init() {}sebagai inisialisasi dari SingletonClass. untuk mencegah instantiate dari luar.
rintaro

46

Swift 1.2 atau yang lebih baru sekarang mendukung variabel / konstanta statis di kelas. Jadi Anda bisa menggunakan konstanta statis:

class MySingleton {

    static let sharedMySingleton = MySingleton()

    private init() {
        // ...
    }
}

35

Ada cara yang lebih baik untuk melakukannya. Anda bisa mendeklarasikan variabel global di kelas Anda di atas deklarasi kelas seperti ini:

var tpScopeManagerSharedInstance = TPScopeManager()

Ini hanya memanggil init default Anda atau variabel init dan global mana pun secara dispatch_oncedefault di Swift. Kemudian di kelas mana pun Anda ingin mendapatkan referensi, Anda cukup melakukan ini:

var refrence = tpScopeManagerSharedInstance
// or you can just access properties and call methods directly
tpScopeManagerSharedInstance.someMethod()

Jadi pada dasarnya Anda dapat menyingkirkan seluruh blok kode instance bersama.


3
Mengapa "var" dan banyak "biarkan"?
Stephan

1
mungkin bisa membiarkan, saya hanya mengujinya dengan var.
Kris Gellci

Saya suka jawaban ini, namun saya perlu mengakses ini (Singleton) dari Interface Builder. Ada ide tentang bagaimana saya bisa mengakses tpScopeManagerSharedInstance ini dari dalam IB ?. Terima kasih.
Luis Palacios

Ini adalah cara yang saya sukai untuk memiliki seorang lajang. Ini memiliki semua fitur yang biasa (keamanan thread & instantiation malas) dan mendukung sintaks yang sangat ringan: tidak perlu menulis TPScopeManager.sharedInstance.doIt()sepanjang waktu, cukup beri nama kelas Anda TPScopeManagerClass, buat deklarasi ini di sebelah kelas public let TPScopeManager = TPScopeManagerClass(), dan saat menggunakan tulis saja TPScopeManager.doIt(). Sangat bersih!
Alex

Tidak ada apa pun di sini untuk mencegah pembuatan instance tambahan TPScopeManager, dan oleh karenanya definisi tersebut bukan singleton .
Caleb

28

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:

class MyClass {

    private static let _sharedInstance = MyClass()

    class func sharedInstance() -> MyClass {
        return _sharedInstance
    }
}

Ambil singleton via MyClass.sharedInstance().


1
terunggah untuk komentar dari LearnCocos2D :), juga untuk gaya.
x4h1d

2
variabel global harus diubah ke variabel kelas melalui statis di dalam kelas.
Malhal

2
@malhal ketika sebuah variabel ditandai pribadi tetapi di luar kelas, itu bukan global - tetapi hanya mencakup file yang ada di dalamnya. Statis di dalam kelas akan bekerja hampir sama, tetapi saya telah memperbarui jawaban untuk menggunakan statis seperti yang Anda sarankan, karena lebih baik mengelompokkan variabel ke kelas jika Anda menggunakan beberapa kelas dalam file.
Ryan

1
"Swift Singletons terpapar dalam kerangka kakao sebagai fungsi kelas" ... Tidak dalam Swift 3. Mereka sekarang biasanya staticproperti.
Rob

17

Per dokumentasi Apple , telah diulang berkali-kali bahwa cara termudah untuk melakukan ini di Swift adalah dengan properti tipe statis:

class Singleton {
    static let sharedInstance = Singleton()
}

Namun, jika Anda mencari cara untuk melakukan pengaturan tambahan di luar panggilan konstruktor sederhana, rahasianya adalah menggunakan penutupan yang segera dipanggil:

class Singleton {
    static let sharedInstance: Singleton = {
        let instance = Singleton()
        // setup code
        return instance
    }()
}

Ini dijamin aman-utas dan diinisialisasi dengan malas hanya sekali.


bagaimana Anda dapat mengatur contoh statis kembali ke nol?
gpichler

1
@ user1463853 - Anda tidak bisa, dan umumnya tidak.
Rob

16

Swift 4+

protocol Singleton: class {
    static var sharedInstance: Self { get }
}

final class Kraken: Singleton {
    static let sharedInstance = Kraken()
    private init() {}
}

2
ini perlu kelas akhir, dapatkah Anda menjelaskan lebih banyak perbedaan, karena saya punya masalah dengan solusi lain singleton dengan struct
Raheel Sadiq

haruskah itu private override init () {}
NSRover

8

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.

struct StaticRank {
    static let shared = RankMapping()
}

class func sharedInstance() -> RankMapping {
    return StaticRank.shared
}

class var shared:RankMapping {
    return StaticRank.shared
}

Saya cukup yakin bahwa hanya menggunakan sintaks statis default ini akan melakukan semua pekerjaan yang mengganggu.
Eonil

sayangnya statika hanya berfungsi di dalam struct, jadi itu sebabnya pola ini.
user2485100

Niat saya adalah bahwa kita tidak harus menggunakan dispatch_oncebarang. Saya bertaruh pada gaya Anda. :)
Eonil

Bukankah classdalam deklarasi kelas sama dengan staticdalam deklarasi struct?
Russell Borogove

@ Sam Ya itu. Lihat entri blog Apple di File dan Inisialisasi yang menjelaskan bahwa baik pengguna global maupun statis dari struct dan enums mendapatkan manfaat dari dispatch_oncekemampuan ini .
Rob

5

Secara singkat,

class Manager {
    static let sharedInstance = Manager()
    private init() {}
}

Anda mungkin ingin membaca File dan Inisialisasi

Inisialisasi lazy untuk variabel global (juga untuk anggota statis struct dan enum) dijalankan pertama kali ketika global diakses, dan diluncurkan dispatch_onceuntuk memastikan bahwa inisialisasi adalah atom.


4

Jika Anda berencana untuk menggunakan kelas Swift singleton Anda di Objective-C, setup ini akan membuat kompiler menghasilkan header Objective-C-like yang sesuai:

class func sharedStore() -> ImageStore {
struct Static {
    static let instance : ImageStore = ImageStore()
    }
    return Static.instance
}

Kemudian di kelas Objective-C Anda dapat memanggil singleton Anda seperti yang Anda lakukan di masa pra-Swift:

[ImageStore sharedStore];

Ini hanya implementasi sederhana saya.


Ini sebenarnya lebih ringkas & benar daripada contoh lainnya karena diterapkan dengan cara yang sama seperti lajang Swift lainnya. yaitu: sebagai fungsi kelas suka NSFileManager.defaultManager(), tetapi masih menggunakan mekanisme anggota statis Swift yang malas thread-safe.
Leslie Godwin 3-15

Kakao umumnya mengimplementasikan ini sebagai properti statis, saat ini, bukan sebagai fungsi kelas.
Rob

Saya sadar akan hal itu, komentar saya sudah lebih dari 2 tahun. Terima kasih telah menyebutkan.
Michael

4

Solusi pertama

let SocketManager = SocketManagerSingleton();

class SocketManagerSingleton {

}

Kemudian dalam kode Anda:

func someFunction() {        
    var socketManager = SocketManager        
}

Solusi kedua

func SocketManager() -> SocketManagerSingleton {
    return _SocketManager
}
let _SocketManager = SocketManagerSingleton();

class SocketManagerSingleton {

}

Dan nanti dalam kode Anda, Anda akan dapat menjaga kawat gigi agar kurang kebingungan:

func someFunction() {        
    var socketManager = SocketManager()        
}

4
final class MySingleton {
     private init() {}
     static let shared = MySingleton()
}

Kalau begitu sebut saja;

let shared = MySingleton.shared

Dilakukan dengan baik untuk tidak hanya menandai initsebagai private, tetapi juga untuk membuat sharedMyModelseperti final! Demi pembaca masa depan, dalam Swift 3, kita mungkin cenderung untuk mengubah nama sharedMyModelmenjadi sederhana shared.
Rob

Ini adalah satu-satunya jawaban yang benar, kecuali bahwa penggantian dan panggilan ke super.init salah dan bahkan tidak dapat dikompilasi.
Michael Morris

4

Menggunakan:

class UtilSingleton: NSObject {

    var iVal: Int = 0

    class var shareInstance: UtilSingleton {
        get {
            struct Static {
                static var instance: UtilSingleton? = nil
                static var token: dispatch_once_t = 0
            }
            dispatch_once(&Static.token, {
                Static.instance = UtilSingleton()
            })
            return Static.instance!
        }
    }
}

Cara Penggunaan:

UtilSingleton.shareInstance.iVal++
println("singleton new iVal = \(UtilSingleton.shareInstance.iVal)")

Ini persis sama dengan salah satu jawaban yang saya lalui dalam perjalanan ke jawaban saat ini. Karena variabel global diinisialisasi malas dan aman, tidak ada alasan untuk kompleksitas tambahan.
David Berry

@ David Selain tidak memiliki variabel global. :)
hpique

@hpique no, persis seperti salah satu upaya saya sebelumnya Lihatlah histori edit.
David Berry

4

Pendekatan terbaik di Swift di atas 1.2 adalah singleton satu baris, karena -

class Shared: NSObject {

    static let sharedInstance = Shared()

    private override init() { }
}

Untuk mengetahui lebih detail tentang pendekatan ini, Anda dapat mengunjungi tautan ini .


Mengapa NSObjectsubkelas? Selain itu, ini tampaknya pada dasarnya sama dengan stackoverflow.com/a/28436202/1187415 .
Martin R

3

Dari Apple Documents (Swift 3.0.1),

Anda cukup menggunakan properti tipe statis, yang dijamin akan diinisialisasi malas hanya sekali, bahkan ketika diakses di banyak utas secara bersamaan:

class Singleton {
    static let sharedInstance = Singleton()
}

Jika Anda perlu melakukan pengaturan tambahan di luar inisialisasi, Anda dapat menetapkan hasil pemanggilan penutupan ke konstanta global:

class Singleton {
    static let sharedInstance: Singleton = {
        let instance = Singleton()
        // setup code
        return instance
    }()
}

3

Saya akan menyarankan enum, seperti yang akan Anda gunakan di Jawa, misalnya

enum SharedTPScopeManager: TPScopeManager {
    case Singleton
}

IMO, ini adalah satu-satunya cara Swift yang benar untuk mengimplementasikan Singleton. jawaban lain adalah ObjC / C / C ++ way
Bryan Chen

Bisakah Anda menguraikan jawaban ini? Tidak jelas bagi saya di mana Singleton berasal dari cuplikan ini
Kenny Winker

@ KennyWinker Saya tidak memiliki login pengembang Apple, oleh karena itu tidak cepat dan jadi saya tidak bisa menjawab ketika inisialisasi terjadi. Di Jawa, ini digunakan pertama kali. Mungkin Anda bisa mencobanya dengan cetak pada inisialisasi dan melihat apakah cetak terjadi pada saat peluncuran atau setelah akses. Itu akan tergantung pada bagaimana enum diimplementasikan oleh kompiler.
Howard Lovatt

@ KennyWinkler: Apple baru saja mengklarifikasi cara kerjanya, lihat developer.apple.com/swift/blog/?id=7 . Di dalamnya mereka mengatakan "jalankan initializer untuk global pertama kali direferensikan, mirip dengan Java" dan khususnya. Mereka juga mengatakan bahwa di bawah selimut mereka menggunakan "dispatch_once untuk memastikan bahwa inisialisasi adalah atom". Oleh karena itu enum hampir pasti cara untuk pergi kecuali jika Anda memiliki beberapa init mewah untuk dilakukan, maka statis statis pribadi adalah solusinya.
Howard Lovatt

2

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!

import Foundation

class ItemStore: NSObject {

    class var sharedStore : ItemStore {
        struct Singleton {
            // lazily initiated, thread-safe from "let"
            static let instance = ItemStore()
        }
        return Singleton.instance
    }

    var _privateItems = Item[]()
    // The allItems property can't be changed by other objects
    var allItems: Item[] {
        return _privateItems
    }

    init() {
        super.init()
        let path = itemArchivePath
        // Returns "nil" if there is no file at the path
        let unarchivedItems : AnyObject! = NSKeyedUnarchiver.unarchiveObjectWithFile(path)

        // If there were archived items saved, set _privateItems for the shared store equal to that
        if unarchivedItems {
            _privateItems = unarchivedItems as Array<Item>
        } 

        delayOnMainQueueFor(numberOfSeconds: 0.1, action: {
            assert(self === ItemStore.sharedStore, "Only one instance of ItemStore allowed!")
        })
    }

    func createItem() -> Item {
        let item = Item.randomItem()
        _privateItems.append(item)
        return item
    }

    func removeItem(item: Item) {
        for (index, element) in enumerate(_privateItems) {
            if element === item {
                _privateItems.removeAtIndex(index)
                // Delete an items image from the image store when the item is 
                // getting deleted
                ImageStore.sharedStore.deleteImageForKey(item.itemKey)
            }
        }
    }

    func moveItemAtIndex(fromIndex: Int, toIndex: Int) {
        _privateItems.moveObjectAtIndex(fromIndex, toIndex: toIndex)
    }

    var itemArchivePath: String {
        // Create a filepath for archiving
        let documentDirectories = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true)
        // Get the one document directory from that list
        let documentDirectory = documentDirectories[0] as String
        // append with the items.archive file name, then return
        return documentDirectory.stringByAppendingPathComponent("items.archive")
    }

    func saveChanges() -> Bool {
        let path = itemArchivePath
        // Return "true" on success
        return NSKeyedArchiver.archiveRootObject(_privateItems, toFile: path)
    }
}

Dan jika Anda tidak mengenali beberapa fungsi-fungsi itu, berikut adalah file utilitas Swift kecil yang pernah saya gunakan:

import Foundation
import UIKit

typealias completionBlock = () -> ()

extension Array {
    func contains(#object:AnyObject) -> Bool {
        return self.bridgeToObjectiveC().containsObject(object)
    }

    func indexOf(#object:AnyObject) -> Int {
        return self.bridgeToObjectiveC().indexOfObject(object)
    }

    mutating func moveObjectAtIndex(fromIndex: Int, toIndex: Int) {
        if ((fromIndex == toIndex) || (fromIndex > self.count) ||
            (toIndex > self.count)) {
                return
        }
        // Get object being moved so it can be re-inserted
        let object = self[fromIndex]

        // Remove object from array
        self.removeAtIndex(fromIndex)

        // Insert object in array at new location
        self.insert(object, atIndex: toIndex)
    }
}

func delayOnMainQueueFor(numberOfSeconds delay:Double, action closure:()->()) {
    dispatch_after(
        dispatch_time(
            DISPATCH_TIME_NOW,
            Int64(delay * Double(NSEC_PER_SEC))
        ),
        dispatch_get_main_queue()) {
            closure()
    }
}

2

Dengan cepat, Anda dapat membuat kelas singleton dengan cara berikut:

class AppSingleton: NSObject {

    //Shared instance of class
    static let sharedInstance = AppSingleton()

    override init() {
        super.init()
    }
}

1

Saya lebih suka implementasi ini:

class APIClient {

}

var sharedAPIClient: APIClient = {
    return APIClient()
}()

extension APIClient {
    class func sharedClient() -> APIClient {
        return sharedAPIClient
    }
}

1

Cara implementasi saya di Swift ...

ConfigurationManager.swift

import Foundation

    let ConfigurationManagerSharedInstance = ConfigurationManager()
 class ConfigurationManager : NSObject {
    var globalDic: NSMutableDictionary = NSMutableDictionary()

class var sharedInstance:ConfigurationManager {
    return ConfigurationManagerSharedInstance

}

init() {

    super.init()

    println ("Config Init been Initiated, this will be called only onece irrespective of many calls")   

}

Akses globalDic dari layar aplikasi mana saja dengan cara di bawah ini.

Baca:

 println(ConfigurationManager.sharedInstance.globalDic)  

Menulis:

 ConfigurationManager.sharedInstance.globalDic = tmpDic // tmpDict is any value that to be shared among the application

1

Satu-satunya pendekatan yang tepat di bawah ini.

final class Singleton {
    static let sharedInstance: Singleton = {
        let instance = Singleton()
        // setup code if anything
        return instance
    }()

    private init() {}
}

Untuk mengakses

let signleton = Singleton.sharedInstance

Alasan:

  • static tipe properti dijamin akan diinisialisasi malas hanya sekali, bahkan ketika diakses di beberapa utas secara bersamaan, jadi tidak perlu menggunakan dispatch_once
  • Privatisasi initmetode sehingga instance tidak dapat dibuat oleh kelas lain.
  • final kelas karena Anda tidak ingin kelas lain mewarisi kelas Singleton.

Mengapa Anda menggunakan inisialisasi penutupan sementara Anda dapat langsung menggunakanstatic let sharedInstance = Singleton()
abhimuralidharan

1
jika Anda tidak ingin melakukan pengaturan tambahan apa pun yang Anda katakan benar.
applefreak

1

Setelah melihat implementasi David, sepertinya tidak perlu memiliki fungsi kelas tunggal instanceMethodkarena letmelakukan hampir sama dengan sharedInstancemetode kelas. Yang perlu Anda lakukan adalah mendeklarasikannya sebagai konstanta global dan hanya itu.

let gScopeManagerSharedInstance = ScopeManager()

class ScopeManager {
   // No need for a class method to return the shared instance. Use the gScopeManagerSharedInstance directly. 
}

2
Seperti yang saya katakan dalam komentar saya, satu-satunya alasan untuk melakukannya adalah bahwa pada titik tertentu di masa depan Anda dapat memindahkan / menyembunyikan variabel global dan mendapatkan lebih banyak perilaku seperti singleton. Pada titik itu, jika semuanya menggunakan pola yang konsisten, Anda bisa mengubah kelas singleton sendiri tanpa harus mengubah penggunaannya.
David Berry

0
   func init() -> ClassA {
    struct Static {
        static var onceToken : dispatch_once_t = 0
        static var instance : ClassA? = nil
    }

    dispatch_once(&Static.onceToken) {
        Static.instance = ClassA()
    }

    return Static.instance!
}

Seperti yang telah dibahas panjang lebar di sini, tidak perlu dengan cepat membungkus inisialisasi dispatch_oncekarena inisialisasi variabel statis malas dan secara otomatis dilindungi melalui dispatch_once Apple sebenarnya merekomendasikan menggunakan statika daripada dispatch_once karena alasan itu.
David Berry

0

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:

class AppManager {
    private static let _sharedInstance = AppManager()

    class func getSharedInstance() -> AppManager {
       return _sharedInstance
    }

    private init() {} // Privatizing the init method
}

// How to use?
AppManager.getSharedInstance()

Metode 2:

class AppManager {
    static let sharedInstance = AppManager()

    private init() {} // Privatizing the init method
}

// How to use?
AppManager.sharedInstance

-1

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

struct DataService {

    private static var _instance : DataService?

    private init() {}   //cannot initialise from outer class

    public static var instance : DataService {
        get {
            if _instance == nil {
                DispatchQueue.global().sync(flags: .barrier) {
                    if _instance == nil {
                        _instance = DataService()
                    }
                }
            }
            return _instance!
        }
    }
}

2
Apa keuntungan dari properti tipe statis (yang dijamin akan diinisialisasi malas hanya sekali, bahkan ketika diakses di banyak utas secara bersamaan)?
Martin R

-1

Saya meminta singleton saya untuk mengizinkan warisan, dan tidak ada solusi yang benar-benar mengizinkannya. Jadi saya datang dengan ini:

public class Singleton {
    private static var sharedInstanceVar = Singleton()

    public class func sharedInstance() -> Singleton {
        return sharedInstanceVar
    }
}


public class SubSingleton: Singleton {

    private static var sharedInstanceToken: dispatch_once_t = 0

    public class override func sharedInstance() -> SubSingleton {
        dispatch_once(&sharedInstanceToken) {
            sharedInstanceVar = SubSingleton()
        }
    return sharedInstanceVar as! SubSingleton
    }
}
  • Dengan cara ini ketika melakukan Singleton.sharedInstance()pertama itu akan mengembalikan instance dariSingleton
  • Ketika melakukan SubSingleton.sharedInstance()pertama itu akan mengembalikan instance yang SubSingletondibuat.
  • Jika di atas dilakukan, maka SubSingleton.sharedInstance()ini Singletonadalah benar dan contoh yang sama digunakan.

Masalah dengan pendekatan kotor pertama ini adalah bahwa saya tidak dapat menjamin bahwa subclass akan mengimplementasikan dispatch_once_tdan memastikan bahwa sharedInstanceVarhanya 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).



-2

Saya menggunakan sintaks berikut:

public final class Singleton {    
    private class func sharedInstance() -> Singleton {
        struct Static {
            //Singleton instance.
            static let sharedInstance = Singleton()
        }
        return Static.sharedInstance
    }

    private init() { }

    class var instance: Singleton {
        return sharedInstance()
    }
}

Ini berfungsi dari Swift 1.2 hingga 4, dan memiliki beberapa keunggulan:

  1. Mengingatkan pengguna untuk tidak membuat subkelas implementasi
  2. Mencegah pembuatan instance tambahan
  3. Memastikan kreasi malas dan instantiasi unik
  4. Mempersingkat sintaks (menghindari ()) dengan mengizinkan untuk mengakses contoh sebagai Singleton.instance
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.