dispatch_once 在 Swift 3 中去哪里?
Whither dispatch_once in Swift 3?
好的,所以我在 Xcode 8 中发现了新的 。我使用 DispatchQueue.main.async
很开心,我一直在浏览 [=14] =] 模块在 Xcode 中查找所有新 API。
但我也使用 dispatch_once
来确保像单例创建和一次性设置这样的事情不会被执行超过一次(即使在多线程环境中)...和 dispatch_once
在新的Dispatch模块里找不到了?
static var token: dispatch_once_t = 0
func whatDoYouHear() {
print("All of this has happened before, and all of it will happen again.")
dispatch_once(&token) {
print("Except this part.")
}
}
自Swift1.x以来,Swift一直在使用dispatch_once
behind the scenes对全局变量和静态属性进行线程安全的惰性初始化。
所以上面的 static var
已经在使用 dispatch_once
,这使得它有点奇怪(并且可能有问题再次使用它作为另一个 dispatch_once
的标记)。事实上有如果没有这种递归,确实没有使用 dispatch_once
的安全方法,所以他们放弃了它。相反,只需使用基于它构建的语言功能:
// global constant: SomeClass initializer gets called lazily, only on first use
let foo = SomeClass()
// global var, same thing happens here
// even though the "initializer" is an immediately invoked closure
var bar: SomeClass = {
let b = SomeClass()
b.someProperty = "whatever"
b.doSomeStuff()
return b
}()
// ditto for static properties in classes/structures/enums
class MyClass {
static let singleton = MyClass()
init() {
print("foo")
}
}
因此,如果您一直使用 dispatch_once
进行一次性 初始化 并产生一些值,那就太好了——您可以将该值设为全局值变量或静态 属性 您正在初始化。
但是,如果您使用 dispatch_once
做的工作不一定有结果怎么办?您仍然可以使用全局变量或静态变量 属性 来做到这一点:只需将该变量的类型设置为 Void
:
let justAOneTimeThing: () = {
print("Not coming back here.")
}()
并且如果访问全局变量或静态 属性 来执行一次性工作对您来说感觉不合适 - 比如说,您希望您的客户在之前调用 "initialize me" 函数他们与您的图书馆合作——只需将访问权包装在一个函数中:
func doTheOneTimeThing() {
justAOneTimeThing
}
有关更多信息,请参阅 migration guide。
编译在Xcode 8 GA Swift 3
创建 dispatch_once 单例 class 实例的推荐优雅方式:
final class TheRoot {
static let shared = TheRoot()
var appState : AppState = .normal
...
使用方法:
if TheRoot.shared.appState == .normal {
...
}
这些行是做什么的?
final - 所以 class 不能被覆盖、扩展,它也使代码比 运行 更快一些,间接寻址更少。
static let shared = TheRoot() - 此行执行惰性初始化,并且仅 运行 一次。
这个解决方案是线程安全的。
根据 Migration Guide:
The free function dispatch_once is no longer available in Swift. In
Swift, you can use lazily initialized globals or static properties and
get the same thread-safety and called-once guarantees as dispatch_once
provided.
示例:
let myGlobal = { … global contains initialization in a call to a closure … }()
// using myGlobal will invoke the initialization code only the first time it is used.
_ = myGlobal
这里和互联网上的其他答案都很好,但我觉得还应该提到这个小花絮:
dispatch_once
的伟大之处在于它的优化程度,本质上是在第一个 运行 之后以一种我几乎无法理解的方式修改代码,但我有理由相信那样会更快而不是设置和检查(真实的)全局令牌。
虽然可以在 Swift 中合理地实现令牌的东西,但必须声明另一个存储的布尔值并不是那么好。更不用说线程不安全了。正如 doc 所说,您应该使用 "lazily initialized global." 是的,但为什么要弄乱全局范围,对吧?
在有人说服我更好的方法之前,我倾向于在我将使用它的范围内声明我的 do-once 闭包,或者合理地接近它,如下所示:
private lazy var foo: Void = {
// Do this once
}()
基本上我是说 "When I read this, foo
should be the result of running this block." 它的行为方式与全局 let
常量完全相同,只是在正确的范围内。而且更漂亮。然后我会在任何我喜欢的地方调用它,把它读成永远不会被用到的东西。为此,我喜欢 Swift 的 _
。像这样:
_ = foo
这个非常酷的怪癖实际上已经存在了一段时间,但还没有得到太多的爱。它基本上在 运行 时间单独留下变量,作为一个未调用的闭包,直到有人想看到它的 Void
结果。在读取时,它调用闭包,将其丢弃并将其结果保存在 foo
中。 Void
在内存方面几乎不使用任何东西,因此后续读取(即 _ = foo
)对 CPU 不做任何事情。 (不要在这方面引用我的话,请有人检查一下程序集以确保!)有多少都行,Swift 基本上在第一个 运行 之后就不再关心它了!丢掉旧的 dispatch_once_t
,并让您的许多代码与圣诞节那天第一次打开它时一样漂亮!
我的一个问题是,您可以在首次读取之前将 foo
设置为其他内容,然后您的代码将 永远不会 被调用!因此,全局 let
常量可以防止这种情况发生。事情是,class 范围内的常量不能很好地与 self
一起玩,所以不要玩实例变量......但是说真的,你什么时候设置 anything还是 Void
??
那个,你需要将 return 类型指定为 Void
或 ()
,否则它仍然会抱怨 self
。谁打的?
而 lazy
只是为了使变量尽可能地惰性化,所以 Swift 不会 运行 它直接在 init()
上运行。
很时髦,只要你记得不要给它写信! :P
Swift 3.0
中“dispatch_once”的示例
第 1 步:只需将下面的代码替换为您的 Singleton.swift(单例 class)
// Singleton Class
class Singleton: NSObject {
var strSample = NSString()
static let sharedInstance:Singleton = {
let instance = Singleton ()
return instance
} ()
// MARK: Init
override init() {
print("My Class Initialized")
// initialized with variable or property
strSample = "My String"
}
}
Singleton Sample Image
第 2 步:从 ViewController.swift
调用单例
// ViewController.swift
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let mySingleton = Singleton.sharedInstance
print(mySingleton.strSample)
mySingleton.strSample = "New String"
print(mySingleton.strSample)
let mySingleton1 = Singleton.sharedInstance
print(mySingleton1.strSample)
}
ViewController Sample Image
这样输出
My Class Initialized
My String
New String
New String
虽然“lazy var”模式让我不再关心调度令牌,而且通常比 dispatch_once()
更方便,但我不喜欢调用站点的样子:
_ = doSomethingOnce
我希望这个语句看起来更像一个函数调用(因为它暗示了动作),但它看起来根本不像。此外,必须编写 _ =
来显式丢弃结果很烦人。
有更好的方法:
lazy var doSomethingOnce: () -> Void = {
print("executed once")
return {}
}()
这使得以下成为可能:
doSomethingOnce()
这可能效率较低(因为它调用一个空闭包而不是仅仅丢弃一个 Void
),但提高清晰度对我来说是完全值得的。
Thread-safe dispatch_once:
private lazy var foo: Void = {
objc_sync_enter(self)
defer { objc_sync_exit(self) }
// Do this once
}()
好的,所以我在 Xcode 8 中发现了新的 DispatchQueue.main.async
很开心,我一直在浏览 [=14] =] 模块在 Xcode 中查找所有新 API。
但我也使用 dispatch_once
来确保像单例创建和一次性设置这样的事情不会被执行超过一次(即使在多线程环境中)...和 dispatch_once
在新的Dispatch模块里找不到了?
static var token: dispatch_once_t = 0
func whatDoYouHear() {
print("All of this has happened before, and all of it will happen again.")
dispatch_once(&token) {
print("Except this part.")
}
}
自Swift1.x以来,Swift一直在使用dispatch_once
behind the scenes对全局变量和静态属性进行线程安全的惰性初始化。
所以上面的 static var
已经在使用 dispatch_once
,这使得它有点奇怪(并且可能有问题再次使用它作为另一个 dispatch_once
的标记)。事实上有如果没有这种递归,确实没有使用 dispatch_once
的安全方法,所以他们放弃了它。相反,只需使用基于它构建的语言功能:
// global constant: SomeClass initializer gets called lazily, only on first use
let foo = SomeClass()
// global var, same thing happens here
// even though the "initializer" is an immediately invoked closure
var bar: SomeClass = {
let b = SomeClass()
b.someProperty = "whatever"
b.doSomeStuff()
return b
}()
// ditto for static properties in classes/structures/enums
class MyClass {
static let singleton = MyClass()
init() {
print("foo")
}
}
因此,如果您一直使用 dispatch_once
进行一次性 初始化 并产生一些值,那就太好了——您可以将该值设为全局值变量或静态 属性 您正在初始化。
但是,如果您使用 dispatch_once
做的工作不一定有结果怎么办?您仍然可以使用全局变量或静态变量 属性 来做到这一点:只需将该变量的类型设置为 Void
:
let justAOneTimeThing: () = {
print("Not coming back here.")
}()
并且如果访问全局变量或静态 属性 来执行一次性工作对您来说感觉不合适 - 比如说,您希望您的客户在之前调用 "initialize me" 函数他们与您的图书馆合作——只需将访问权包装在一个函数中:
func doTheOneTimeThing() {
justAOneTimeThing
}
有关更多信息,请参阅 migration guide。
编译在Xcode 8 GA Swift 3
创建 dispatch_once 单例 class 实例的推荐优雅方式:
final class TheRoot {
static let shared = TheRoot()
var appState : AppState = .normal
...
使用方法:
if TheRoot.shared.appState == .normal {
...
}
这些行是做什么的?
final - 所以 class 不能被覆盖、扩展,它也使代码比 运行 更快一些,间接寻址更少。
static let shared = TheRoot() - 此行执行惰性初始化,并且仅 运行 一次。
这个解决方案是线程安全的。
根据 Migration Guide:
The free function dispatch_once is no longer available in Swift. In Swift, you can use lazily initialized globals or static properties and get the same thread-safety and called-once guarantees as dispatch_once provided.
示例:
let myGlobal = { … global contains initialization in a call to a closure … }()
// using myGlobal will invoke the initialization code only the first time it is used.
_ = myGlobal
这里和互联网上的其他答案都很好,但我觉得还应该提到这个小花絮:
dispatch_once
的伟大之处在于它的优化程度,本质上是在第一个 运行 之后以一种我几乎无法理解的方式修改代码,但我有理由相信那样会更快而不是设置和检查(真实的)全局令牌。
虽然可以在 Swift 中合理地实现令牌的东西,但必须声明另一个存储的布尔值并不是那么好。更不用说线程不安全了。正如 doc 所说,您应该使用 "lazily initialized global." 是的,但为什么要弄乱全局范围,对吧?
在有人说服我更好的方法之前,我倾向于在我将使用它的范围内声明我的 do-once 闭包,或者合理地接近它,如下所示:
private lazy var foo: Void = {
// Do this once
}()
基本上我是说 "When I read this, foo
should be the result of running this block." 它的行为方式与全局 let
常量完全相同,只是在正确的范围内。而且更漂亮。然后我会在任何我喜欢的地方调用它,把它读成永远不会被用到的东西。为此,我喜欢 Swift 的 _
。像这样:
_ = foo
这个非常酷的怪癖实际上已经存在了一段时间,但还没有得到太多的爱。它基本上在 运行 时间单独留下变量,作为一个未调用的闭包,直到有人想看到它的 Void
结果。在读取时,它调用闭包,将其丢弃并将其结果保存在 foo
中。 Void
在内存方面几乎不使用任何东西,因此后续读取(即 _ = foo
)对 CPU 不做任何事情。 (不要在这方面引用我的话,请有人检查一下程序集以确保!)有多少都行,Swift 基本上在第一个 运行 之后就不再关心它了!丢掉旧的 dispatch_once_t
,并让您的许多代码与圣诞节那天第一次打开它时一样漂亮!
我的一个问题是,您可以在首次读取之前将 foo
设置为其他内容,然后您的代码将 永远不会 被调用!因此,全局 let
常量可以防止这种情况发生。事情是,class 范围内的常量不能很好地与 self
一起玩,所以不要玩实例变量......但是说真的,你什么时候设置 anything还是 Void
??
那个,你需要将 return 类型指定为 Void
或 ()
,否则它仍然会抱怨 self
。谁打的?
而 lazy
只是为了使变量尽可能地惰性化,所以 Swift 不会 运行 它直接在 init()
上运行。
很时髦,只要你记得不要给它写信! :P
Swift 3.0
中“dispatch_once”的示例第 1 步:只需将下面的代码替换为您的 Singleton.swift(单例 class)
// Singleton Class
class Singleton: NSObject {
var strSample = NSString()
static let sharedInstance:Singleton = {
let instance = Singleton ()
return instance
} ()
// MARK: Init
override init() {
print("My Class Initialized")
// initialized with variable or property
strSample = "My String"
}
}
Singleton Sample Image
第 2 步:从 ViewController.swift
调用单例// ViewController.swift
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let mySingleton = Singleton.sharedInstance
print(mySingleton.strSample)
mySingleton.strSample = "New String"
print(mySingleton.strSample)
let mySingleton1 = Singleton.sharedInstance
print(mySingleton1.strSample)
}
ViewController Sample Image
这样输出
My Class Initialized
My String
New String
New String
虽然“lazy var”模式让我不再关心调度令牌,而且通常比 dispatch_once()
更方便,但我不喜欢调用站点的样子:
_ = doSomethingOnce
我希望这个语句看起来更像一个函数调用(因为它暗示了动作),但它看起来根本不像。此外,必须编写 _ =
来显式丢弃结果很烦人。
有更好的方法:
lazy var doSomethingOnce: () -> Void = {
print("executed once")
return {}
}()
这使得以下成为可能:
doSomethingOnce()
这可能效率较低(因为它调用一个空闭包而不是仅仅丢弃一个 Void
),但提高清晰度对我来说是完全值得的。
Thread-safe dispatch_once:
private lazy var foo: Void = {
objc_sync_enter(self)
defer { objc_sync_exit(self) }
// Do this once
}()