Swift'te dispatch_once singleton modeli kullanma


575

Swift'te kullanmak için uygun bir singleton modeli yapmaya çalışıyorum. Şimdiye kadar, iş parçacığı olmayan güvenli bir model olarak çalışmak mümkün:

class var sharedInstance: TPScopeManager {
    get {
        struct Static {
            static var instance: TPScopeManager? = nil
        }

        if !Static.instance {
            Static.instance = TPScopeManager()
        }

        return Static.instance!
    }
}

Tekil örneğin Statik yapıya sarılması, karmaşık adlandırma şemaları olmadan tekil örneklerle çarpışmayan tek bir örneğe izin vermeli ve işleri oldukça özel yapmalıdır. Açıkçası, bu model iş parçacığı için güvenli değildir. Bu yüzden dispatch_onceher şeye eklemeye çalıştım :

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!
    }
}

Ama dispatch_oncehatta derleyici hatası alıyorum :

İfadenin 'Void' türü '()' türüne dönüştürülemiyor

Sözdiziminin birkaç farklı varyantını denedim, ancak hepsi aynı sonuçlara sahip gibi görünüyor:

dispatch_once(Static.token, { Static.instance = TPScopeManager() })

dispatch_onceSwift'i kullanmanın uygun kullanımı nedir ? Başlangıçta sorun ()hata mesajı nedeniyle blok ile olduğunu düşündüm , ama ne kadar çok bakarsam, o kadar dispatch_once_tdoğru doğru tanımlanması bir sorun olabilir düşünüyorum .


3
Tüm bu statik kodu kaldırmak ve bir @lazy başlatıcısı ile salt okunur bir özelliği kullanın.
Sulthan

1
Demek istediğim şey o. Maalesef hala iç kısımlar hakkında yeterli bilgiye sahip değiliz. Ancak, IMHO'nun herhangi bir uygulaması @lazyiş parçacığı için güvenli olmalıdır.
Sulthan

1
Ve bu şekilde, uygulamayı arayanların yırtıcılarına maruz bırakmamak da avantajlıdır.
David Berry

1
Ayrıca @lazy sınıf değişkenlerine sahip olabileceğiniz gibi görünmüyor.
David Berry

Dikkatli ol! Bu yaklaşımla dikkat edilmesi gereken iki şey. İlk olarak, bundan devralan sınıfların sharedInstance özelliğini geçersiz kılması gerekir. Static.instance = TPScopeManager()örnek türünü zorlar. Gibi bir şey kullanırsanStatic.instance = self()Gerekli bir başlatıcı ile benzer bir , uygun tip sınıfı oluşturulur. Yine de, bu önemli bir şey, hiyerarşideki tüm örnekler için sadece bir kez! Başlatılacak ilk tür, tüm örnekler için ayarlanan türdür. Ben obj-c'nin de aynı şekilde davrandığını düşünmüyorum.
sean woodward

Yanıtlar:


713

tl; dr: Swift 1.2 veya üstü ve iç içe geçmiş yapı kullanıyorsanız sınıf sabit yaklaşımını kullanın önceki sürümleri desteklemeniz gerekiyorsa yaklaşımını kullanın.

Swift ile yaşadığım deneyimden, tembel başlatma ve iplik güvenliğini destekleyen Singleton modelini uygulamak için üç yaklaşım var.

Sınıf sabiti

class Singleton  {
   static let sharedInstance = Singleton()
}

Bu yaklaşım tembel başlatmayı destekler çünkü Swift sınıf sabitlerini (ve değişkenleri) tembel olarak başlatır ve tanımıyla iş parçacığı için güvenlidir let. Bu artık resmen önerilen yol tek birtonu örneklemenin .

Sınıf sabitleri Swift 1.2'de tanıtıldı. Swift'in önceki bir sürümünü desteklemeniz gerekiyorsa, aşağıdaki iç içe yapı yaklaşımını veya genel bir sabiti kullanın.

İç içe yapı

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

Burada içiçe bir yapının statik sabitini sınıf sabiti olarak kullanıyoruz. Bu, Swift 1.1 ve önceki sürümlerinde statik sınıf sabitlerinin bulunmaması için bir çözümdür ve yine de işlevlerde statik sabitlerin ve değişkenlerin bulunmaması için bir çözüm olarak çalışır.

dispatch_once

Geleneksel Objective-C yaklaşımı Swift'e taşındı. İç içe geçmiş yapıya göre hiçbir avantajı olmadığından eminim ama sözdizimindeki farklılıkları ilginç bulduğum için buraya koyuyorum.

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!
    }
}

Birim testleri için bu GitHub projesine bakın .


13
"izin sayesinde iplik güvenli" - bu herhangi bir yerde belirtildi mi? Belgelerde bundan bahsedemiyorum.
jtbandes

4
@jtbandes Sabitler bildiğim tüm dillerde iş parçacığı açısından güvenlidir.
hpique

2
@DaveWood Sanırım son yaklaşımdan bahsediyorsunuz. Kendime şu sözleri söyleyeceğim: "Bu yaklaşımı kullanmanın artık gerekli olmadığını söyleyebilirim, ancak sözdizimindeki farklılıkları ilginç bulduğum için yine de buraya koyuyorum."
hpique

5
Should initda beyan edilmesi gereken privateteminat birine ve nesne yalnızca bir örneği şimdiye uygulamanın ömrü boyunca var olacak?
Andrew

5
"Sınıf sabiti" yaklaşımında, (a) sınıfın finalalt sınıfta kalmamanız için sınıf ilan edilmesi ; ve (b) inityöntemi privatebaşka bir yerde yanlışlıkla başka bir örneği başlatamayacağınız şekilde bildirmek .
Rob

175

Apple artık statik yapı değişkenlerinin hem tembel hem de sarılı olarak başlatıldığını açıkladığı için (yazının dispatch_oncesonundaki nota bakın), son çözümümün şöyle olacağını düşünüyorum:

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

        return Singleton.instance
    }
}

Bu, statik yapı elemanlarının otomatik tembel, iplik korumalı başlatılmasından yararlanır, gerçek uygulamayı tüketiciden güvenli bir şekilde gizler, her şeyi okunabilirlik için kompakt bir şekilde bölümlere ayırır ve görünür bir global değişkeni ortadan kaldırır.

Apple tembel başlatıcıların iş parçacığı açısından güvenli olduğunu açıkladı, bu yüzden dispatch_oncebenzer korumalara veya benzeri korumalara gerek yok

Genel değişken için tembel başlatıcı (ayrıca yapıların ve numaralandırmaların statik üyeleri için) genel olarak ilk kez erişildiğinde çalıştırılır ve başlatmanın atomik olduğundan emin olmak için dispatch_once olarak başlatılır. Bu, kodunuzda dispatch_once kullanmanın harika bir yolunu sağlar: bir başlatıcı ile genel bir değişken tanımlayın ve onu özel olarak işaretleyin.

Gönderen burada


1
Onaylamak için: global değişkenler tembel, iş parçacığı için güvenli başlatma içerir, ancak sınıf değişkenleri yoktur. Sağ?
Bill

14
İyi bir uygulamanın, başlatıcıyı özel olarak ilan etmek olacağını da ekleyeceğim: private init() {}Bu sınıfın dışarıdan başlatılması anlamına gelmediğini daha da güçlendirmek için.
Pascal Bourque

1
böylece statik yapı var başlatma tembel ve iş parçacığı güvenlidir, ya statik yapı var çoktonlu bir sözlükse, o zaman her erişim için manuel olarak çağrıları senkronize / sıraya gerekir, değil mi?

Sorunuzu doğru anlarsam, sözlük ve dizi erişimleri doğası gereği iş parçacığı açısından güvenli değildir, bu nedenle bir tür iş parçacığı senkronizasyonu kullanmanız gerekir.
David Berry

@DavidBerry Bu singleton sınıfının içindeki bir işlevi nasıl çağırmalıyım? MyClass.sharedInstance'ın ilk çağrısında çağrılacak bir işleve ihtiyacım var.
Ameet Dhas

163

Swift 1.2 ve sonrası için:

class Singleton  {
   static let sharedInstance = Singleton()
}

Doğruluk kanıtıyla (tüm kredi buraya gelir ), önceki tektonlar için önceki yöntemlerden herhangi birini kullanmak için çok az neden yoktur.

Güncelleme : Artık resmi dokümanlarda açıklandığı gibi singletonları tanımlamanın resmi yolu bu !

staticVs kullanımı ile ilgili endişeler gelince class. staticbile kullanacak kişi olmalıclassdeğişkenler kullanılabilir kullanılacak olan olmalıdır. Tektonların alt sınıflara alınması amaçlanmamıştır, çünkü bu, taban tek tonunun birden çok örneğiyle sonuçlanır. kullanmastaticBunu güzel, hızlı bir şekilde uygular.

Swift 1.0 ve 1.1 için:

Swift'teki son değişikliklerle, çoğunlukla yeni erişim kontrol yöntemleri ile, şimdi tekiltonlar için küresel bir değişken kullanmanın daha temiz bir yoluna yöneliyorum.

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

Buradaki Swift blog makalesinde belirtildiği gibi :

Genel değişken için tembel başlatıcı (ayrıca yapıların ve numaralandırmaların statik üyeleri için) genel olarak ilk kez erişildiğinde çalıştırılır ve başlatmanın atomik olduğundan emin olmak için dispatch_once olarak başlatılır. Bu, kodunuzda dispatch_once kullanmanın harika bir yolunu sağlar: bir başlatıcı ile genel bir değişken tanımlayın ve onu özel olarak işaretleyin.

Tek birton oluşturmanın bu yolu, iplik güvenli, hızlı, tembeldir ve ayrıca ObjC'ye ücretsiz olarak köprülenir.


2
Yalnızca bu yanıtı okuyan herkes: Simgeyi statik yapmayı unutmayın, aksi takdirde davranış tanımsızdır. Kodun tamamı için David'in düzenlenmiş sorusuna bakın.
nschum

@nschum, aksi takdirde, davranış tanımsız değildir, sadece iyi tanımlanmış bir şekilde bozulur: blok her zaman yürütülür.
Michael

@Michael: Dokümantasyonda tanımsız olduğunu belirtiyor. Mevcut davranış bu nedenle tesadüfidir.
nschum

1
Söylemesi tuhaf bir şey. Dokümantasyon "tanımsız" olarak adlandırılırsa, bu sadece kodu yazan kişi ne yaptığına dair herhangi bir söz vermez. Değişkenin statik olup olmadığını bilen kodla ilgisi yoktur. Bu sadece şu anki (veya görünen) davranışa güvenilemeyeceği anlamına gelir.
nschum

6
private init() {}Başlatıcısı olarak eklemek isteyebilirsiniz SingletonClass. dışarıdan anlıkları önlemek için.
rintaro

46

Swift 1.2 veya üstü artık sınıflardaki statik değişkenleri / sabitleri desteklemektedir. Böylece sadece statik bir sabit kullanabilirsiniz:

class MySingleton {

    static let sharedMySingleton = MySingleton()

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

35

Bunu yapmanın daha iyi bir yolu var. Sınıfınızda global bir değişkeni, sınıf bildiriminin üzerinde şöyle bildirebilirsiniz:

var tpScopeManagerSharedInstance = TPScopeManager()

Bu, yalnızca varsayılan initinizi veya hangi init'i ve global değişkenleri dispatch_oncevarsayılan olarak Swift'te çağırır . Sonra hangi sınıfta referans almak isterseniz, bunu yapın:

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

Temel olarak paylaşılan örnek kod bloğunun tamamından kurtulabilirsiniz.


3
Neden bir "var" ve bir "let" lotu?
Stephan

1
belki bir izin olabilir, ben sadece bir var ile test.
Kris Gellci

Bu yanıtı beğendim, ancak bu (Singleton) Arayüz Oluşturucu'dan erişmem gerekiyor. Bu tpScopeManagerSharedInstance'a IB içinden nasıl erişebileceğim hakkında herhangi bir fikir ?. Teşekkürler.
Luis Palacios

Bu benim tek bir tona sahip olmayı tercih ettiğim yol. Her zamanki özelliklere sahiptir (iplik güvenliği ve tembel örnekleme) ve çok hafif bir sözdizimini destekler: TPScopeManager.sharedInstance.doIt()her zaman yazmaya gerek yok , sadece sınıfınızı adlandırın, sınıfın TPScopeManagerClassyanında bu bildirime sahip olun public let TPScopeManager = TPScopeManagerClass()ve sadece yazın TPScopeManager.doIt(). Çok temiz!
Alex

Burada ek örneklerin yaratılmasını engelleyecek hiçbir şey yoktur TPScopeManagerve bu nedenle tanım gereği tekil değildir .
Caleb

28

Swift singletons örneğin sınıf fonksiyonları gibi Kakao çerçeveler içinde maruz kaldıkları NSFileManager.defaultManager(), NSNotificationCenter.defaultCenter(). Bu nedenle, diğer bazı çözümler gibi bir sınıf değişkeni yerine, bu davranışı yansıtmak için bir sınıf işlevi olarak daha anlamlı olur. Örneğin:

class MyClass {

    private static let _sharedInstance = MyClass()

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

İle singletonu alın MyClass.sharedInstance().


1
Stil için de LearnCocos2D :) yorumuna oy verdi.
x4h1d

2
global değişken, sınıf içindeki bir statik üzerinden bir sınıf değişkenine değiştirilmelidir.
malhal

2
@malhal, bir değişken özel olarak işaretlendiğinde ancak bir sınıfın dışındaysa, küresel değildir - ancak yalnızca içinde bulunduğu dosyaya dahil edilir. Sınıf içindeki bir statik hemen hemen aynı şekilde çalışır, ancak statikin kullanılması için cevabı güncelledim önerdiğiniz gibi, dosyada birden çok sınıf kullanırsanız, değişkeni sınıfa göre daha iyi gruplandırır.
Ryan

1
"Swift Singleton kakao çerçevelerinde sınıf işlevleri olarak gösteriliyor" ... Swift 3'te değil. Artık genellikle staticözellikler.
Rob

17

Başına Elma belgelerinde , bu Swift bunu yapmanın en kolay yolu statik tip özelliğiyle olduğunu birçok kez tekrar edilmiştir:

class Singleton {
    static let sharedInstance = Singleton()
}

Ancak, basit bir yapıcı çağrısının ötesinde ek kurulum gerçekleştirmenin bir yolunu arıyorsanız, sır derhal çağrılan bir kapatma kullanmaktır:

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

Bu, güvenli bir şekilde garanti edilir ve tembel olarak sadece bir kez başlatılır.


statik let örneğini nil'e nasıl geri ayarlayabilirsiniz?
gpichler

1
@ user1463853 - Yapamazsınız ve genellikle yapmamalısınız.
Rob

16

Swift 4+

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

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

2
Bu son sınıfa ihtiyaç duyar, daha fazla farkı açıklayabilir misiniz, çünkü struct ile singleton'un diğer çözümü ile sorunum var
Raheel Sadiq

özel geçersiz kılma init () {}
NSRover

8

Apple'ın örnek koduna baktığımda bu modelle karşılaştım. Swift'in statikle nasıl başa çıkacağından emin değilim, ancak bu C # 'da güvenli iş parçacığı olacaktır. Objective-C interop için hem özelliği hem de yöntemi dahil ediyorum.

struct StaticRank {
    static let shared = RankMapping()
}

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

class var shared:RankMapping {
    return StaticRank.shared
}

Sadece bu varsayılan statik sözdizimini kullanmanın tüm can sıkıcı işleri yapacağından eminim.
Eonil

ne yazık ki statik sadece yapıların içinde çalışır, bu yüzden bu örüntü.
user2485100

Niyetim, dispatch_oncemalzeme kullanmak zorunda olmamamızdı. Tarzınıza bahis oynuyorum. :)
Eonil

classSınıf bildirimi staticiçinde bir yapı bildirimi ile eşdeğer değil mi?
Russell Borogove

@ Evet evet öyle. Yapıların ve numaralandırmaların hem genel hem de statik üyelerinin bu özellikten yararlandığını açıkça belirten Dosyalar ve Başlatma hakkında Apple blog girişine bakın dispatch_once.
Rob

5

Kısaca,

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

Dosyaları ve Başlatma'yı okumak isteyebilirsiniz

Genel bir değişken için tembel başlatıcı (ayrıca yapıların ve numaralandırmaların statik üyeleri için) genel olarak ilk kez erişildiğinde çalıştırılır dispatch_onceve başlatmanın atomik olduğundan emin olmak için başlatılır.


4

Swift singleton sınıfınızı Objective-C'de kullanmayı planlıyorsanız, bu kurulum derleyicinin uygun Objective-C benzeri başlıklar oluşturmasını sağlayacaktır:

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

Daha sonra Objective-C sınıfında singleton'unuzu Swift öncesi günlerde yaptığınız gibi çağırabilirsiniz:

[ImageStore sharedStore];

Bu sadece benim basit uygulamam.


Bu aslında diğer örnekten daha kısa ve doğrudur çünkü diğer Swift tektonlarıyla aynı şekilde uygulanır. yani: class fonksiyonları olarak ister NSFileManager.defaultManager(), ama yine de Swift tembel evreli statik üyesi mekanizmalarını kullanır.
Leslie Godwin

Kakao, bunları günümüzde sınıf fonksiyonları olarak değil, statik özellikler olarak uygulamaktadır.
Rob

Bunun farkındayım, yorumum 2 yaşın üzerinde. Bahsettiğiniz için teşekkürler.
Michael

4

İlk çözüm

let SocketManager = SocketManagerSingleton();

class SocketManagerSingleton {

}

Daha sonra kodunuzda:

func someFunction() {        
    var socketManager = SocketManager        
}

İkinci çözüm

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

class SocketManagerSingleton {

}

Ve daha sonra kodunuzda daha az karışıklık için parantez tutabilirsiniz:

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

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

Sonra ara;

let shared = MySingleton.shared

Eh sadece işaretleme değil için yapılır initolarak privatedeğil, aynı zamanda yapmak için sharedMyModelolduğu gibi final! Gelecekteki okurlar uğruna, Swift 3'te, sharedMyModelbasitçe yeniden adlandırmaya meyilli olabiliriz shared.
Rob

Bu tek doğru cevaptır, ancak super.init'e geçersiz kılma ve çağrı hatalı ve derlenmeyecektir.
Michael Morris

4

kullanın:

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!
        }
    }
}

Nasıl kullanılır:

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

Bu, şimdiki cevaba giderken yaşadığım cevaplardan biriyle aynı. Global değişkenler hem tembel hem de iş parçacığı için güvenli olduğundan başlatıldığından, ek karmaşıklık için bir neden yoktur.
David Berry

@David Global değişkene sahip olmanın dışında. :)
hpique

@hpique hayır, tıpkı önceki girişimlerimden biri gibi. Düzenleme geçmişine bakın.
David Berry

4

Swift'in 1.2'nin üzerindeki en iyi yaklaşımı, tek satırlık bir singleton,

class Shared: NSObject {

    static let sharedInstance = Shared()

    private override init() { }
}

Bu yaklaşım hakkında daha fazla bilgi edinmek için bu bağlantıyı ziyaret edebilirsiniz .


Neden bir NSObjectalt sınıf? Bunun dışında, bu aslında stackoverflow.com/a/28436202/1187415 ile aynı görünüyor .
Martin R

3

Apple Docs'tan (Swift 3.0.1),

Birden çok iş parçacığına aynı anda erişilse bile, yalnızca bir kez tembel olarak başlatılacağı garanti edilen statik bir tür özelliğini kullanabilirsiniz:

class Singleton {
    static let sharedInstance = Singleton()
}

Başlatma işleminin ötesinde ek kurulum yapmanız gerekiyorsa, bir küresel sabitlemeye bir kapatma çağrısının sonucunu atayabilirsiniz:

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

3

Bir öneririm enumJava, örneğin kullanmak gibi,

enum SharedTPScopeManager: TPScopeManager {
    case Singleton
}

IMO, Singleton'u uygulamanın tek doğru Swift yoludur. diğer cevaplar ObjC / C / C ++ way
Bryan Chen

Bu cevabı biraz açıklayabilir misiniz? Singleton'un bu
pasajdan

@KennyWinker Apple geliştirici girişim yok, bu yüzden hızlı değil ve bu yüzden başlatma sırasında cevap veremiyorum. Java'da ilk kullanımdadır. Belki de başlatma sırasında bir baskı ile deneyebilir ve baskının başlatma sırasında mı yoksa erişimden sonra mı gerçekleştiğini görebilirsiniz. Enum'un derleyici tarafından nasıl uygulandığına bağlı olacaktır.
Howard Lovatt

@KennyWinkler: Apple bunun nasıl çalıştığını açıkladı, bkz. Developer.apple.com/swift/blog/?id=7 . İçinde "Java'ya benzer şekilde, ilk kez bir global için başlatıcıyı çalıştır" derler ve özellikle. Ayrıca kapakların altında "başlatmanın atomik olduğundan emin olmak için dispatch_once" kullandıklarını söylüyorlar. Bu nedenle enum, yapmak için bazı süslü initiniz yoksa, kesinlikle özel bir yol, o zaman özel bir statik izin çözümdür.
Howard Lovatt

2

Sadece referans olarak, Jack Wu / hpique'in Nested Struct uygulamasının bir Singleton uygulaması örneği. Uygulama, arşivlemenin nasıl çalışabileceğini ve eşlik eden bazı işlevleri de gösterir. Bunu bir örnekle bulamadım, umarım bu birine yardımcı olur!

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)
    }
}

Ve bu işlevlerden bazılarını tanımadıysanız, işte kullandığım küçük yaşayan bir Swift yardımcı programı dosyası:

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

Hızlı bir şekilde, aşağıdaki şekilde tek bir sınıf oluşturabilirsiniz:

class AppSingleton: NSObject {

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

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

1

Bu uygulamayı tercih ederim:

class APIClient {

}

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

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

1

Swift'te uygulama şeklim ...

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")   

}

GlobalDic'e uygulamanın herhangi bir ekranından aşağıdan erişin.

oku:

 println(ConfigurationManager.sharedInstance.globalDic)  

Yazmak:

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

1

Tek doğru yaklaşım aşağıdadır.

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

    private init() {}
}

Erişim

let signleton = Singleton.sharedInstance

Nedenleri:

  • static type özelliği, aynı anda birden çok iş parçacığına erişilse bile, yalnızca bir kez tembel olarak başlatılacağı garanti edilir, bu nedenle dispatch_once
  • initYöntemi özelleştirmek, böylece örnek diğer sınıflar tarafından oluşturulamaz.
  • final Başka sınıfların Singleton sınıfını devralmasını istemediğiniz için sınıf.

Neden doğrudan kullanırken kapatma başlatmayı kullandınızstatic let sharedInstance = Singleton()
abhimuralidharan

1
herhangi bir ek kurulum yapmak istemiyorsanız, söyledikleriniz doğrudur.
applefreak

1

Bir tek sınıf tesiri olduğu gerek yoktur gibi David'in uygulanmasını gördükten sonra, öyle görünüyor instanceMethodçünkü letbir şekilde hemen hemen aynı şeyi yapıyor sharedInstancesınıf yöntemiyle. Yapmanız gereken tek şey onu küresel bir sabit olarak ilan etmek.

let gScopeManagerSharedInstance = ScopeManager()

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

2
Yorumlarımda söylediğim gibi, bunu yapmanın tek nedeni gelecekte bir noktada küresel değişkeni hareket ettirebilir / gizleyebilir ve daha fazla tekil davranışa sahip olabilirsiniz. Bu noktada, eğer her şey tutarlı bir örüntü kullanıyorsa, kullanımı değiştirmek zorunda kalmadan singleton sınıflarını kendileri değiştirebilirsiniz.
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!
}

Burada büyük ölçüde tartışıldığı gibi, dispatch_oncestatik değişken başlatma tembel olduğu ve dispatch_once Apple aracılığıyla otomatik olarak korunduğu için başlatmayı hızlı bir şekilde sarmak gerekli değildir , bu nedenle dispatch_once yerine statik kullanılmasını önerir.
David Berry

0

Geçmişte singleton gerçekleştirme çabası üç yoldan başka bir şey değildir: küresel değişkenler, iç değişkenler ve dispatch_once yolları.

İşte iki iyi singleton. (Not: init () özelleştirme yöntemine ne tür bir yazı yazılması gerektiğine bakılmaksızın. , nesneyi oluşturmak için varsayılan başlatma yöntemiyle bu sınıfın diğer nesnelerini '()' önleyin.)

Yöntem 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()

Yöntem 2:

class AppManager {
    static let sharedInstance = AppManager()

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

// How to use?
AppManager.sharedInstance

-1

Bu, güvenli iş parçacığı özelliklerine sahip en basit olanıdır. Başka hiçbir iş parçacığı isteseler bile aynı singleton nesnesine erişemez. Hızlı 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
Statik tip özelliğe göre avantajı nedir?
Martin R

-1

Singleton'umun mirasa izin vermesini istedim ve bu çözümlerin hiçbiri buna izin vermedi. Ben de bununla geldim:

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
    }
}
  • Bu şekilde Singleton.sharedInstance()ilk yaptığınızdaSingleton
  • Yaparken SubSingleton.sharedInstance()öncelikle örneğini döndürür SubSingletonyarattı.
  • Yukarıda yapılırsa, o zaman SubSingleton.sharedInstance()olduğu Singletondoğrudur ve aynı örneği kullanılır.

Bu ilk kirli yaklaşımla ilgili sorun, alt sınıfların uygulanacağını garanti edemem dispatch_once_tve sharedInstanceVarsınıf başına sadece bir kez değiştirildiğinden emin olmam .

Bunu daha da hassaslaştırmaya çalışacağım, ancak buna karşı güçlü duygulara sahip olup olmadığını görmek ilginç olacaktır (bunun yanı sıra ayrıntılı ve manuel olarak güncellenmesini gerektiriyor).



-2

Aşağıdaki sözdizimini kullanıyorum:

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()
    }
}

Bu, Swift 1.2'den 4'e kadar çalışır ve çeşitli avantajları vardır:

  1. Kullanıcıya alt sınıf uygulamasını kullanmamasını hatırlatır
  2. Ek örneklerin oluşturulmasını önler
  3. Tembel yaratım ve benzersiz örnekleme sağlar
  4. Örneğe şu şekilde erişilmesine izin vererek sözdizimini kısaltır (avoids ()) Singleton.instance
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.