2012-02-02 18 views
295

Lý do chính xác để sử dụng dispatch_once trong trình truy cập cá thể chia sẻ của một singleton trong ARC là gì?Tại sao Apple khuyên bạn nên sử dụng dispatch_once để triển khai mẫu đơn lẻ trong ARC?

+ (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; 
} 

Không phải là một ý tưởng tồi để khởi tạo singleton không đồng bộ trong nền? Tôi có nghĩa là những gì sẽ xảy ra nếu tôi yêu cầu trường hợp được chia sẻ và dựa vào nó ngay lập tức, nhưng dispatch_once mất đến Giáng sinh để tạo đối tượng của tôi? Nó không trở lại ngay lập tức? Ít nhất đó dường như là toàn bộ quan điểm của Grand Central Dispatch.

Vậy tại sao họ làm điều này?

+0

'Lưu ý: biến tĩnh và toàn cầu mặc định là 0. – ikkentim

Trả lời

406

dispatch_once() hoàn toàn đồng bộ. Không phải tất cả các phương thức GCD đều làm việc không đồng bộ (trường hợp tại điểm, dispatch_sync() là đồng bộ). Việc sử dụng dispatch_once() thay thế thành ngữ sau:

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

Lợi ích của dispatch_once() trên này là nó nhanh hơn. Nó cũng sạch hơn về mặt ngữ nghĩa, vì nó cũng bảo vệ bạn khỏi nhiều luồng làm phân bổ init của sharedInstance của bạn - nếu tất cả chúng đều cố gắng vào cùng một thời điểm chính xác. Nó sẽ không cho phép hai trường hợp được tạo ra. Toàn bộ ý tưởng của dispatch_once() là "thực hiện một cái gì đó một lần và chỉ một lần", đó là chính xác những gì chúng tôi đang làm.

+4

Vì lợi ích của đối số, tôi cần lưu ý rằng [tài liệu] (https://developer.apple.com/library/mac/documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html#//apple_ref/c/ func/dispatch_once) không nói rằng nó đang được thực hiện đồng bộ. Nó chỉ nói rằng nhiều invocations đồng thời sẽ được serialized. – expert

+5

Bạn cho rằng nó nhanh hơn - nhanh hơn bao nhiêu? Tôi không có lý do gì để nghĩ rằng bạn không nói sự thật, nhưng tôi muốn thấy một điểm chuẩn đơn giản. –

+29

Tôi vừa làm một điểm chuẩn đơn giản (trên iPhone 5) và có vẻ như dispatch_once nhanh gấp 2 lần so với @synchronized. –

39

Vì nó sẽ chỉ chạy một lần. Vì vậy, nếu bạn thử và truy cập nó hai lần từ các chủ đề khác nhau, nó sẽ không gây ra vấn đề gì.

Mike Ash có mô tả đầy đủ trong bài đăng trên blog Care and Feeding of Singletons của mình.

Không phải tất cả các khối GCD đều chạy không đồng bộ.

+11

Kevin là một câu trả lời hay hơn, nhưng tôi để tôi giữ liên kết tới bài đăng của Mike Ash. – Abizern