Apple neden ARC altında tekli kalıbı uygulamak için dispatch_once kullanılmasını öneriyor?


305

ARC altında bir singletonun paylaşılan örnek erişimcisinde dispatch_once kullanmanın tam nedeni nedir?

+ (MyClass *)sharedInstance
{
    //  Static local predicate must be initialized to 0
    static MyClass *sharedInstance = nil;
    static dispatch_once_t onceToken = 0;
    dispatch_once(&onceToken, ^{
        sharedInstance = [[MyClass alloc] init];
        // Do any other initialisation stuff here
    });
    return sharedInstance;
}

Singleton'u arka planda eşzamansız olarak başlatmak kötü bir fikir değil mi? Bu paylaşılan örneği talep edip hemen güvenirsem ne olur, ama dispatch_once nesnemi oluşturmak için Noel'e kadar sürer? Hemen geri dönmez değil mi? En azından Grand Central Dispatch'in bütün mesele bu gibi görünüyor.

Peki bunu neden yapıyorlar?


Note: static and global variables default to zero.
ikkentim

Yanıtlar:


418

dispatch_once()kesinlikle eşzamanlıdır. Tüm GCD yöntemleri işleri eşzamansız olarak yapmaz (bu durumda dispatch_sync(), eşzamanlıdır). Kullanımı dispatch_once()yerine geçer aşağıdaki deyim:

+ (MyClass *)sharedInstance {
    static MyClass *sharedInstance;
    @synchronized(self) {
        if (sharedInstance == nil) {
            sharedInstance = [[MyClass alloc] init];
        }
    }
    return sharedInstance;
}

Bunun yararı, dispatch_once()daha hızlı olmasıdır. Aynı zamanda anlamsal olarak daha temizdir, çünkü sizi aynı anda denerse, sizi sharedInstance öğenizi ayırma yapan birden çok iş parçacığından korur. İki örneğin oluşturulmasına izin vermez. Tüm fikri dispatch_once()"bir şeyi sadece bir kez gerçekleştir" dir, ki tam olarak bunu yapıyoruz.


4
Tartışma uğruna, belgelerin eşzamanlı olarak yürütüldüğünü söylemediğine dikkat etmeliyim . Sadece aynı anda birden fazla çağrının serileştirileceğini söylüyor.
uzman

5
Daha hızlı olduğunu iddia ediyorsun - ne kadar hızlı? Doğruyu söylemediğini düşünmek için hiçbir nedenim yok, ama basit bir kıyaslama görmek istiyorum.
Joshua Gross

29
Sadece basit bir kıyaslama yaptım (iPhone 5'te) ve dispatch_once'ın @synchronized'den yaklaşık 2 kat daha hızlı olduğu anlaşılıyor.
Joshua Gross

3
@ReneDohan: Hiç kimse bu yöntemi farklı bir iş parçacığından çağırmaz% 100 eminseniz işe yarıyor. Ancak dispatch_once()kullanımı gerçekten basittir (özellikle Xcode bunu sizin için tam bir kod snippet'inde otomatik olarak tamamlayacağından) ve yöntemin iş parçacığı açısından güvenli olması gerekip gerekmediğini asla düşünmeniz gerekmediği anlamına gelir.
Lily Ballard

1
@siuying: Aslında bu doğru değil. İlk olarak, +initializepaylaşılan örneğinizi henüz oluşturmaya çalışmasanız bile, yapılan herhangi bir şey sınıfa dokunmadan önce olur. Genel olarak, tembel başlatma (sadece gerektiğinde bir şey yaratmak) daha iyidir. İkincisi, performans talebiniz bile doğru değil. dispatch_once()şeklindeki zamanın neredeyse aynı miktarda alır if (self == [MyClass class])içinde +initialize. Zaten bir varsa +initialize, evet paylaşılan örneği oluşturmak daha hızlı, ama çoğu sınıf yok.
Lily Ballard

41

Çünkü sadece bir kez çalışır. Bu nedenle, farklı iş parçacıklarından iki kez erişmeye çalışırsanız, soruna neden olmaz.

Mike Ash, Singletons Bakımı ve Beslemesi blog yazısında tam bir açıklamaya sahiptir .

Tüm GCD blokları eşzamansız olarak çalıştırılmaz.


Lily daha iyi bir cevap, ama Mike Ash'in gönderisine bağlantıyı korumak için benimkini bırakıyorum.
Abizern
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.