Apple的样本MVCN网络中的Singleton类NetworkManager是否正确?

时间:2012-04-06 05:56:06

标签: objective-c ios

以下是示例代码http://developer.apple.com/library/ios/#samplecode/MVCNetworking/Introduction/Intro.html

的链接

以下是 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;
}

显然这里存在线程安全问题。当存在多个线程时,可以创建两个NetworkManager实例。所以Apple犯了一个错误,对吗?

3 个答案:

答案 0 :(得分:2)

是的,你是对的。它会在并发环境中出现问题。更好的方法是在alloc之前使用仔细检查:

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

有很多方法可以使用Ojbective-C编写单例,请查看这篇文章:What should my Objective-C singleton look like?

<强>更新

BobCromwell是对的。 apple不推荐使用double check lock,这是Apple的Threading Programming Guide

中的文档
  

双重检查锁是一种尝试通过在锁定之前测试锁定标准来减少锁定的开销。由于双重检查锁可能不安全,系统不会为它们提供明确的支持,因此不鼓励使用它们。

答案 1 :(得分:1)

是的,这是错的。从sNetworkManager开始为nil,并考虑两个线程T1和T2。

一种可能的情况是:

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有一些更安全的方法。

答案 2 :(得分:0)

此代码中没有错误。仅创建一个sNetworkManager,原因很简单,即使用“静态”一词。这里使用static关键字将变量定义为全局变量,但仅对该函数可见。该变量在+(NetworkManager *)sharedManager的第一次调用中分配,然后它不再为null而不再被初始化。