dispatch_once
语言版本3进行更改后,Swift中的新语法是什么?旧版本如下。
var token: dispatch_once_t = 0
func test() {
dispatch_once(&token) {
}
}
这些是对libdispatch所做的更改。
dispatch_once
语言版本3进行更改后,Swift中的新语法是什么?旧版本如下。
var token: dispatch_once_t = 0
func test() {
dispatch_once(&token) {
}
}
这些是对libdispatch所做的更改。
pod 'SwiftDispatchOnce', '~> 1.0'
干杯。:]
Answers:
从文档:
派遣
自由功能dispatch_once在Swift中不再可用。在Swift中,您可以使用延迟初始化的全局变量或静态属性,并获得与提供的dispatch_once相同的线程安全性和一次调用保证。例:
let myGlobal: () = { … global contains initialization in a call to a closure … }()
_ = myGlobal // using myGlobal will invoke the initialization code only the first time it is used.
dispatch_once
很清楚。这不幸的是,丑陋和混乱..
虽然使用惰性初始化的全局变量可以进行一次初始化,但对于其他类型则没有意义。对于诸如单例之类的东西使用惰性初始化的全局变量是很有意义的,而对于诸如防止麻烦的设置之类的事情则没有太大的意义。
这是一个Swift 3样式的dispatch_once实现:
public extension DispatchQueue {
private static var _onceTracker = [String]()
/**
Executes a block of code, associated with a unique token, only once. The code is thread safe and will
only execute the code once even in the presence of multithreaded calls.
- parameter token: A unique reverse DNS style name such as com.vectorform.<name> or a GUID
- parameter block: Block to execute once
*/
public class func once(token: String, block:@noescape(Void)->Void) {
objc_sync_enter(self); defer { objc_sync_exit(self) }
if _onceTracker.contains(token) {
return
}
_onceTracker.append(token)
block()
}
}
这是一个示例用法:
DispatchQueue.once(token: "com.vectorform.test") {
print( "Do This Once!" )
}
或使用UUID
private let _onceToken = NSUUID().uuidString
DispatchQueue.once(token: _onceToken) {
print( "Do This Once!" )
}
由于我们目前正处于从Swift 2到3的过渡时期,因此下面是一个Swift 2实现示例:
public class Dispatch
{
private static var _onceTokenTracker = [String]()
/**
Executes a block of code, associated with a unique token, only once. The code is thread safe and will
only execute the code once even in the presence of multithreaded calls.
- parameter token: A unique reverse DNS style name such as com.vectorform.<name> or a GUID
- parameter block: Block to execute once
*/
public class func once(token token: String, @noescape block:dispatch_block_t) {
objc_sync_enter(self); defer { objc_sync_exit(self) }
if _onceTokenTracker.contains(token) {
return
}
_onceTokenTracker.append(token)
block()
}
}
objc_sync_enter
和objc_sync_exit
了。
在上述Tod Cunningham的答案的基础上,我添加了另一种方法,该方法可自动从文件,函数和行中生成令牌。
public extension DispatchQueue {
private static var _onceTracker = [String]()
public class func once(file: String = #file,
function: String = #function,
line: Int = #line,
block: () -> Void) {
let token = "\(file):\(function):\(line)"
once(token: token, block: block)
}
/**
Executes a block of code, associated with a unique token, only once. The code is thread safe and will
only execute the code once even in the presence of multithreaded calls.
- parameter token: A unique reverse DNS style name such as com.vectorform.<name> or a GUID
- parameter block: Block to execute once
*/
public class func once(token: String,
block: () -> Void) {
objc_sync_enter(self)
defer { objc_sync_exit(self) }
guard !_onceTracker.contains(token) else { return }
_onceTracker.append(token)
block()
}
}
因此,调用起来可能更简单:
DispatchQueue.once {
setupUI()
}
并且您仍然可以根据需要指定令牌:
DispatchQueue.once(token: "com.hostname.project") {
setupUI()
}
我想如果两个模块中有相同的文件,可能会发生冲突。太可惜了没有#module
编辑
简单的解决方案是
lazy var dispatchOnce : Void = { // or anyName I choose
self.title = "Hello Lazy Guy"
return
}()
使用像
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
_ = dispatchOnce
}
如果添加桥接头,仍然可以使用它:
typedef dispatch_once_t mxcl_dispatch_once_t;
void mxcl_dispatch_once(mxcl_dispatch_once_t *predicate, dispatch_block_t block);
然后在.m
某个地方:
void mxcl_dispatch_once(mxcl_dispatch_once_t *predicate, dispatch_block_t block) {
dispatch_once(predicate, block);
}
您现在应该可以在mxcl_dispatch_once
Swift中使用了。
通常,您应该使用Apple的建议,但是我有一些合法的用途,我需要dispatch_once
在两个函数中使用单个令牌,而Apple所提供的却没有涵盖。
您可以像这样声明一个顶级变量函数:
private var doOnce: ()->() = {
/* do some work only once per instance */
return {}
}()
然后在任何地方调用:
doOnce()
雨燕3:对于那些喜欢可重用的类(或结构)的人:
public final class /* struct */ DispatchOnce {
private var lock: OSSpinLock = OS_SPINLOCK_INIT
private var isInitialized = false
public /* mutating */ func perform(block: (Void) -> Void) {
OSSpinLockLock(&lock)
if !isInitialized {
block()
isInitialized = true
}
OSSpinLockUnlock(&lock)
}
}
用法:
class MyViewController: UIViewController {
private let /* var */ setUpOnce = DispatchOnce()
override func viewWillAppear() {
super.viewWillAppear()
setUpOnce.perform {
// Do some work here
// ...
}
}
}
更新(2017年4月28日):OSSpinLock
被os_unfair_lock
macOS SDK 10.12中的到期弃用警告取代。
public final class /* struct */ DispatchOnce {
private var lock = os_unfair_lock()
private var isInitialized = false
public /* mutating */ func perform(block: (Void) -> Void) {
os_unfair_lock_lock(&lock)
if !isInitialized {
block()
isInitialized = true
}
os_unfair_lock_unlock(&lock)
}
}
OSSpinLock
替换为os_unfair_lock
。顺便说一句:这是关于WWDC的一个很好的视频,关于Concurrent Programming
:developer.apple.com/videos/play/wwdc2016/720
我改善以上答案得到结果:
import Foundation
extension DispatchQueue {
private static var _onceTracker = [AnyHashable]()
///only excute once in same file&&func&&line
public class func onceInLocation(file: String = #file,
function: String = #function,
line: Int = #line,
block: () -> Void) {
let token = "\(file):\(function):\(line)"
once(token: token, block: block)
}
///only excute once in same Variable
public class func onceInVariable(variable:NSObject, block: () -> Void){
once(token: variable.rawPointer, block: block)
}
/**
Executes a block of code, associated with a unique token, only once. The code is thread safe and will
only execute the code once even in the presence of multithreaded calls.
- parameter token: A unique reverse DNS style name such as com.vectorform.<name> or a GUID
- parameter block: Block to execute once
*/
public class func once(token: AnyHashable,block: () -> Void) {
objc_sync_enter(self)
defer { objc_sync_exit(self) }
guard !_onceTracker.contains(token) else { return }
_onceTracker.append(token)
block()
}
}
extension NSObject {
public var rawPointer:UnsafeMutableRawPointer? {
get {
Unmanaged.passUnretained(self).toOpaque()
}
}
}
如果使用的是Swift 1.2或更高版本,请使用类常量方法;如果需要支持早期版本,请使用嵌套的struct方法。在Swift中探索Singleton模式。下面的所有方法都支持延迟初始化和线程安全。Swift 3.0无法使用dispatch_once方法
方法A:类常量
class SingletonA {
static let sharedInstance = SingletonA()
init() {
println("AAA");
}
}
方法B:嵌套结构
class SingletonB {
class var sharedInstance: SingletonB {
struct Static {
static let instance: SingletonB = SingletonB()
}
return Static.instance
}
}
方法C:dispatch_once
class SingletonC {
class var sharedInstance: SingletonC {
struct Static {
static var onceToken: dispatch_once_t = 0
static var instance: SingletonC? = nil
}
dispatch_once(&Static.onceToken) {
Static.instance = SingletonC()
}
return Static.instance!
}
}