在Swift中使用dispatch_once单例模型

本文探讨了如何在Swift中使用dispatch_once实现线程安全的单例模型,通过不同版本的Swift代码示例展示了单例的创建方法。作者遇到的编译错误以及社区提供的解决方案也被详细讨论,包括Swift 1.2以后的官方推荐方法。
摘要由CSDN通过智能技术生成

本文翻译自:Using a dispatch_once singleton model in Swift

I'm trying to work out an appropriate singleton model for usage in Swift. 我正在尝试制定一个合适的单例模型以在Swift中使用。 So far, I've been able to get a non-thread safe model working as: 到目前为止,我已经能够获得一个非线程安全模型,其工作方式如下:

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

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

        return Static.instance!
    }
}

Wrapping the singleton instance in the Static struct should allow a single instance that doesn't collide with singleton instances without complex naming schemings, and it should make things fairly private. 在静态结构中包装单例实例应该允许在没有复杂命名方案的情况下不会与单例实例冲突的单个实例,这应该使事情变得相当私有。 Obviously though, this model isn't thread-safe. 但是,显然,此模型不是线程安全的。 So I tried to add dispatch_once to the whole thing: 所以我试图将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!
    }
}

But I get a compiler error on the dispatch_once line: 但是我在dispatch_once行上收到编译器错误:

Cannot convert the expression's type 'Void' to type '()' 无法将表达式的类型“无效”转换为类型“()”

I've tried several different variants of the syntax, but they all seem to have the same results: 我尝试了几种不同的语法变体,但它们似乎都具有相同的结果:

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

What is the proper usage of dispatch_once using Swift? 使用Swift的dispatch_once的正确用法是什么? I initially thought the problem was with the block due to the () in the error message, but the more I look at it, the more I think it may be a matter of getting the dispatch_once_t correctly defined. 最初,我认为问题是由于错误消息中的()造成的,但是我越看越多,我认为可能是正确定义dispatch_once_t的问题。


#1楼

参考:https://stackoom.com/question/1cnsj/在Swift中使用dispatch-once单例模型


#2楼

For Swift 1.2 and beyond: 对于Swift 1.2及更高版本:

class Singleton  {
   static let sharedInstance = Singleton()
}

With a proof of correctness (all credit goes here ), there is little to no reason now to use any of the previous methods for singletons. 有了正确性的证明(所有功劳都放在这里 ),现在几乎没有理由对单例使用任何先前的方法。

Update : This is now the official way to define singletons as described in the official docs ! 更新 :这是官方文档中定义单例的官方方式!

As for concerns on using static vs class . 至于使用static vs class static should be the one to use even when class variables become available. 即使class变量可用, static应该是使用的一种。 Singletons are not meant to be subclassed since that would result in multiple instances of the base singleton. 单例不打算被子类化,因为这将导致基本单例的多个实例。 Using static enforces this in a beautiful, Swifty way. 使用static以一种美观,迅捷的方式强制执行此操作。

For Swift 1.0 and 1.1: 对于Swift 1.0和1.1:

With the recent changes in Swift, mostly new access control methods, I am now leaning towards the cleaner way of using a global variable for singletons. 随着Swift的最新变化(主要是新的访问控制方法),我现在倾向于使用更简单的方法来为单例使用全局变量。

private let _singletonInstance = SingletonClass()
class SingletonClass {
  class var sharedInstance: SingletonClass {
    return _singletonInstance
  }
}

As mentioned in the Swift blog article here : 随着斯威夫特博客文章中提到在这里

The lazy initializer for a global variable (also for static members of structs and enums) is run the first time that global is accessed, and is launched as dispatch_once to make sure that the initialization is atomic. 全局变量(也适用于结构和枚举的静态成员)的惰性初始化程序在首次访问global时运行,并作为dispatch_once启动,以确保初始化是原子的。 This enables a cool way to use dispatch_once in your code: just declare a global variable with an initializer and mark it private. 这提供了一种在代码中使用dispatch_once的好方法:只需使用初始化程序声明全局变量并将其标记为私有即可。

This way of creating a singleton is thread safe, fast, lazy, and also bridged to ObjC for free. 这种创建单例的方法是线程安全,快速,懒惰的,并且还免费桥接到ObjC。


#3楼

Looking at Apple's sample code I came across this pattern. 在查看苹果的示例代码时,我遇到了这种模式。 I'm not sure how Swift deals with statics, but this would be thread safe in C#. 我不确定Swift如何处理静态,但这在C#中是线程安全的。 I include both the property and method for Objective-C interop. 我同时包含了Objective-C互操作的属性和方法。

struct StaticRank {
    static let shared = RankMapping()
}

class func sharedInstance() -> RankMapping {
    return StaticRank.shared
}

class var shared:RankMapping {
    return StaticRank.shared
}

#4楼

There is a better way to do it. 有更好的方法可以做到这一点。 You can declare a global variable in your class above the class declaration like this: 您可以在类声明上方的类中声明全局变量,如下所示:

var tpScopeManagerSharedInstance = TPScopeManager()

This just calls your default init or whichever init and global variables are dispatch_once by default in Swift. 这只是调用您的默认init,或者默认情况下,Swift中所有init和全局变量都是dispatch_once Then in whichever class you want to get a reference, you just do this: 然后,无论您要获取参考的哪个类,都可以执行以下操作:

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

So basically you can get rid of the entire block of shared instance code. 因此,基本上,您可以摆脱共享实例代码的整个块。


#5楼

Since Apple has now clarified that static struct variables are initialized both lazy and wrapped in dispatch_once (see the note at the end of the post), I think my final solution is going to be: 由于Apple现在已经澄清了静态结构变量在lazy和包装在dispatch_once都被初始化(请参阅文章末尾的注释),所以我认为我的最终解决方案将是:

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

        return Singleton.instance
    }
}

This takes advantage of the automatic lazy, thread-safe initialization of static struct elements, safely hides the actual implementation from the consumer, keeps everything compactly compartmentalized for legibility, and eliminates a visible global variable. 这利用了静态结构元素的自动懒惰,线程安全的初始化,对使用者安全地隐藏实际实现,使所有内容紧凑地划分为易于辨认并消除了可见的全局变量的优势。

Apple has clarified that lazy initializer are thread-safe, so there's no need for dispatch_once or similar protections 苹果已经澄清了惰性初始化器是线程安全的,因此不需要dispatch_once或类似的保护

The lazy initializer for a global variable (also for static members of structs and enums) is run the first time that global is accessed, and is launched as dispatch_once to make sure that the initialization is atomic. 全局变量(也适用于结构和枚举的静态成员)的惰性初始化程序在首次访问global时运行,并作为dispatch_once启动,以确保初始化是原子的。 This enables a cool way to use dispatch_once in your code: just declare a global variable with an initializer and mark it private. 这提供了一种在代码中使用dispatch_once的好方法:只需使用初始化程序声明全局变量并将其标记为私有即可。

From here 这里


#6楼

Use: 使用:

class UtilSingleton: NSObject {

    var iVal: Int = 0

    class var shareInstance: UtilSingleton {
        get {
            struct Static {
                static var instance: UtilSingleton? = nil
                static var token: dispatch_once_t = 0
            }
            dispatch_once(&Static.token, {
                Static.instance = UtilSingleton()
            })
            return Static.instance!
        }
    }
}

How to use: 如何使用:

UtilSingleton.shareInstance.iVal++
println("singleton new iVal = \(UtilSingleton.shareInstance.iVal)")
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值