Swift 3中dispatch_once的位置
好的,所以我在Xcode 8中发现了新的Swifty Dispatch API 。我使用DispatchQueue.main.async
获得了乐趣,并且我一直在Xcode的Dispatch
模块中查找所有新的API。
但我也使用dispatch_once
来确保像单身人士创build和一次性设置不会执行多次(即使在multithreading环境中)… dispatch_once
无处可查找新的调度模块?
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.") } }
自从Swift 1.x以来,Swift一直在后台使用dispatch_once
来执行全局variables和静态属性的线程安全的惰性初始化。
所以上面的static var
已经使用dispatch_once
,这使得它有点奇怪(可能有问题的再次使用它作为另一个dispatch_once
的标记。事实上,没有这种recursion没有安全的方式来使用dispatch_once
,所以他们得到摆脱它,而只是使用它的语言function:
// 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
进行一次性初始化 ,那么这就非常好了,你可以把这个值作为你正在初始化的全局variables或静态属性。
但是如果你使用dispatch_once
做不一定有结果的工作呢? 你仍然可以用全局variables或静态属性来做到这一点:只要使该variables的types为Void
:
let justAOneTimeThing: () = { print("Not coming back here.") }()
而如果访问一个全局variables或静态属性来执行一次性工作对你来说并不合适 – 比方说,你希望你的客户在他们使用你的库之前调用一个“初始化我”函数 – 在一个函数中访问:
func doTheOneTimeThing() { justAOneTimeThing }
有关详情,请参阅迁移指南 。
这里和周围的其他答案相当不错,但我觉得这个小珍闻也应该提到:
关于dispatch_once
好处在于它是如何优化的,基本上是在第一次运行之后以一种我几乎不能理解的方式来修改代码,但是我们可以合理地确信,这比设置和检查(真实)全局令牌要快得多。
虽然令牌的东西可以合理地在Swift中实现,但是不得不声明另一个存储的布尔值并不是那么好。 更不用说线程不安全了。 正如文件所说,你应该使用“懒惰初始化的全球”。 是的,但为什么混乱了全球范围呢?
除非有人说服我有更好的方法,否则我倾向于在我将要使用的范围内或相当接近的范围内声明我的do-onceclosures,如下所示:
private lazy var foo: Void = { // Do this once }()
基本上我是说“当我读到这个时, foo
应该是运行这个块的结果。” 它的行为方式与全球常数一样,就在正确的范围内。 而且更漂亮 然后,我会把它叫到任何我想要的地方,通过阅读它将永远不会被使用的东西。 我喜欢Swift的。 像这样:
_ = foo
这真的很酷的怪癖实际上已经有一段时间了,但并没有看到太多的爱。 它基本上是在运行时单独保留variables,作为一个未调用的闭包,直到某些东西想要看到它的Void
结果。 在阅读时,它调用闭包,把它扔掉,并保持其结果。 Void
几乎没有使用任何内存,所以后续的读取(即_ = foo
)在CPU上不做任何事情。 (请不要在此引用我的意思,有人请在程序集上检查一下!)尽可能多的,Swift基本上在第一次运行后就不再关心了! 丢失那个旧的dispatch_once_t
,并保持你的代码很多,当你第一次打开它的圣诞节!
我的一个问题是你可以在第一次读之前将foo
设置为别的东西,然后你的代码将永远不会被调用! 因此,全球let
恒定,这阻止了。 事情是,类范围内的常量不能很好地与self
玩,所以没有玩实例variables…但是,严重的是,什么时候你设置任何 Void
?
那么,你需要指定返回types为Void
或()
,否则它仍然会抱怨self
。 Who'da thunk?
lazy
只是把它变成懒惰,所以Swift不会直接在init()
上运行它。
相当时髦,只要你记得不要写信给它! :P
Swift 3.0中的“dispatch_once”示例
第一步:用下面的代码replaceSingleton.swift(Singleton类)
// 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" } }
单例样本图像
第2步:从ViewController.swift调用Singleton
// 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示例图像
像这样的输出
My Class Initialized My String New String New String
编译在Xcode 8 GA Swift 3下
build议和优雅的方式来创build一个dispatch_once单例类实例:
final class TheRoot { static let shared = TheRoot() var appState : AppState = .normal ...
要使用它:
if TheRoot.shared.appState == .normal { ... }
这些线是做什么的?
最后 – 所以这个类不能被覆盖,扩展,这也使代码运行速度更快,间接性更低。
static let shared = TheRoot() – 这行代码是惰性的,它只运行一次。
此解决scheme是线程安全的。
虽然“lazy var”模式允许我停止关心调度令牌,并且通常比dispatch_once()
更方便,但我不喜欢它在调用站点上的样子:
_ = doSomethingOnce
我希望这个语句看起来更像是一个函数调用(因为它意味着行动),但它看起来并不那么重要。 而且,必须写_ =
来明确地放弃结果是不必要的,也是令人讨厌的。
有一个更好的方法:
lazy var doSomethingOnce: () -> Void = { print("executed once") return {} }()
这使得以下可能:
doSomethingOnce()
这可能效率较低(因为它调用一个空的封闭,而不是放弃一个Void
),但提高清晰度是完全值得的。
根据移民指南 :
免费函数dispatch_once在Swift中不再可用。 在Swift中,您可以使用lazily初始化的全局variables或静态属性,并获得与dispatch_once提供的相同的线程安全性和调用一次性保证。
例:
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