2012-04-06 25 views
5

Dưới đây là liên kết tới các mẫu mã http://developer.apple.com/library/ios/#samplecode/MVCNetworking/Introduction/Intro.htmlSingleton Class NetworkManager trong MVCNetworking của Apple có đúng không?

Dưới đây là đoạn mã từ tập tin NetworkManager.m

+ (NetworkManager *)sharedManager 
// See comment in header. 
{ 
    static NetworkManager * sNetworkManager; 

    // This can be called on any thread, so we synchronise. We only do this in 
    // the sNetworkManager case because, once sNetworkManager goes non-nil, it can 
    // never go nil again. 

    if (sNetworkManager == nil) { 
     @synchronized (self) { 
      sNetworkManager = [[NetworkManager alloc] init]; 
      assert(sNetworkManager != nil); 
     } 
    } 
    return sNetworkManager; 
} 

Rõ ràng là có chủ đề vấn đề an toàn ở đây. Hai phiên bản NetworkManager có thể được tạo khi có nhiều hơn một luồng. Vì vậy, Apple đã phạm sai lầm, phải không?

+0

"Rõ ràng hai sNetworkManager có thể được tạo ra. Vì vậy, Apple đã thực hiện một sai lầm, phải không?" Tại sao? –

+0

Xin hãy xem câu trả lời của Kurt. –

Trả lời

1

Có, không đúng. Bắt đầu với sNetworkManagernil và xem xét hai chuỗi T1 và T2.

Một có thể, nếu không, kịch bản là:

T1: Determines (sNetworkManager == nil) is true 
T2: Determines (sNetworkManager == nil) is true 
T1: Takes the @synchronized lock 
    Creates a NetworkManager 
    Sets sNetworkManager 
    Releases the lock 
T2: Takes the @synchronized lock 
    Creates a NetworkManager 
    Sets sNetworkManager, LEAKING the first one 
    Releases the lock 

This question có một số cách an toàn hơn để làm việc đó.

0

Không có lỗi trong mã này. Chỉ một sNetworkManager được tạo ra vì một lý do đơn giản là từ "tĩnh" được sử dụng. Từ khóa tĩnh được sử dụng ở đây để xác định biến là toàn cầu nhưng chỉ hiển thị với hàm đó. Biến được phân bổ trong lần gọi đầu tiên của + (NetworkManager *) sharedManager sau đó nó không còn null và không được khởi tạo nữa.

+0

Hi Guy, hãy nhìn vào câu trả lời của Kurt, bạn có thể thêm ý kiến ​​của bạn vào đó nếu bạn nghĩ rằng phân tích của Kurt là không chính xác. –

+0

Phân tích của ông là đúng nhưng không chắc rằng kịch bản này xảy ra. Bạn có nói về chủ đề này về lỗi an toàn trong tin nhắn đầu tiên của bạn không? Tôi nghĩ bạn nói về từ khóa "tĩnh". – user1316852

2

Có, bạn đã đúng. Nó sẽ có vấn đề trong môi trường tương tranh. Cách tốt hơn là sử dụng kiểm tra lại trước khi alloc:

+ (NetworkManager *)sharedManager 
{ 
    static NetworkManager * sNetworkManager; 
    if (sNetworkManager == nil) { 
     @synchronized (self) { 
      if (sNetworkManager == nil) { 
       sNetworkManager = [[NetworkManager alloc] init]; 
       assert(sNetworkManager != nil); 
      } 
     } 
    } 
    return sNetworkManager; 
} 

Và có rất nhiều cách để viết singleton sử dụng Ojbective-C, kiểm tra bài đăng này: What should my Objective-C singleton look like?

Cập nhật

BobCromwell là đúng . Các double check lock không được khuyến khích bởi táo, các tài liệu táo của Threading Programming Guide:

Một khóa kiểm tra lại là một nỗ lực để giảm chi phí tham gia một khóa bằng cách kiểm tra các tiêu chí khóa trước khi tham gia khóa. Bởi vì ổ khóa đôi kiểm tra là có khả năng không an toàn, hệ thống không cung cấp hỗ trợ rõ ràng cho họ và việc sử dụng chúng là discouraged.`

+0

tôi nghĩ rằng khóa kiểm tra kép không chính xác và không được hỗ trợ trên cả iOS trên máy Mac. –

+0

@BobCromwell Tại sao? Bạn có thể cho tôi một số lý do hoặc tham khảo? – tangqiaoboy

+1

trong các từ đơn giản, sNetwokManager có thể không phải là không trước khi đối tượng được hoàn toàn init-ed. Hãy tìm kiếm Double-checkedlock trong tài liệu của Apple "Threading Programming Guide" và đây là một bài viết về điều này: http://www.wincent.com/a/knowledge-base/archives/2006/01/locking_doublec.php –

Các vấn đề liên quan