我正在努力制定一个合适的单例模型用于Swift的使用。到目前为止,我已经能够得到一个非线程安全的模型工作为:

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

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

        return Static.instance!
    }
}

将单例实例包装在Static结构中应该允许单个实例不与单例实例发生冲突,而不需要复杂的命名方案,并且它应该使事情相当私密。但是,这个模型显然不是线程安全的。所以我尝试将dispatch_once添加到整个事情中:

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

但是我在dispatch_once行上得到了一个编译器错误:

不能将表达式的类型“Void”转换为类型“()”

我尝试了几种不同的语法变体,但它们似乎都有相同的结果:

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

在Swift中dispatch_once的正确用法是什么?我最初认为问题出在错误消息中的()块上,但我看得越多,就越觉得可能是正确定义dispatch_once_t的问题。


当前回答

Swift 1.2或更高版本现在支持类中的静态变量/常量。所以你可以使用一个静态常数:

class MySingleton {

    static let sharedMySingleton = MySingleton()

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

其他回答

我使用以下语法:

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

从Swift 1.2到Swift 4都可以使用,有几个优点:

提醒用户不要子类化实现 防止创建额外的实例 确保惰性创建和唯一实例化 通过允许以Singleton.instance的形式访问instance来缩短语法(avoid ())

由于苹果现在已经澄清了静态结构变量是惰性初始化和包装在dispatch_once(见文章末尾的注释),我认为我的最终解决方案将是:

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

        return Singleton.instance
    }
}

这利用了静态结构元素的自动惰性、线程安全初始化,对使用者安全地隐藏了实际的实现,保持所有内容紧凑地分隔以使其易于阅读,并消除了可见的全局变量。

苹果已经澄清了惰性初始化器是线程安全的,所以不需要dispatch_once或类似的保护

全局变量(也包括struct和enum的静态成员)的惰性初始化器在第一次访问global时运行,并作为dispatch_once启动,以确保初始化是原子的。这为在代码中使用dispatch_once提供了一种很酷的方式:只需声明一个带有初始化式的全局变量并将其标记为private。

从这里

有更好的办法。你可以像这样在你的类声明上面声明一个全局变量:

var tpScopeManagerSharedInstance = TPScopeManager()

这只是调用你的默认init或任何init和全局变量是dispatch_once在Swift默认。然后在任何你想要引用的类中,你只需要这样做:

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

所以基本上你可以去掉整个共享实例代码块。

从Apple Docs (Swift 3.0.1),

您可以简单地使用静态类型属性,它保证为 惰性初始化仅一次,即使跨多个访问 同时线程:

class Singleton {
    static let sharedInstance = Singleton()
}

如果需要执行初始化之外的其他设置,可以这样做 将闭包调用的结果赋值给全局变量 不变:

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

简而言之,

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

您可能需要阅读文件和初始化

的静态成员的惰性初始化式 Structs和enum)在第一次访问global时运行,并且 作为dispatch_once启动,以确保初始化为 原子。