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'
乾杯。:]
回答:
ドキュメントから:
ディスパッチ
無料の関数dispatch_onceはSwiftでは使用できなくなりました。Swiftでは、遅延初期化されたグローバルまたは静的プロパティを使用して、dispatch_onceが提供するのと同じスレッドセーフおよびcall-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
明確でした。これは、残念ながら..醜いと紛らわしいです
遅延初期化グローバルを使用することは、一度の初期化には意味がありますが、他のタイプには意味がありません。シングルトンのようなものに怠惰な初期化されたグローバルを使用することは非常に理にかなっていますが、スウィズル設定を保護するようなものにはあまり意味がありません。
以下は、dispatch_onceのSwift3スタイルの実装です。
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への移行の時期にあるため、swift2の実装例を次に示します。
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
ません。
上記のTodCunninghamの回答を拡張して、ファイル、関数、および行からトークンを自動的に作成する別のメソッドを追加しました。
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()
}
2つのモジュールに同じファイルがあると、衝突が発生する可能性があると思います。残念ながらありません#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
ありますが、2つの関数で単一のトークンを使用する必要があり、Appleが代わりに提供するものでカバーされていない正当な使用法がいくつかありました。
次のようにトップレベルの変数関数を宣言できます。
private var doOnce: ()->() = {
/* do some work only once per instance */
return {}
}()
次に、これをどこにでも呼び出します。
doOnce()
Swift 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日):macOS SDK10.12で非推奨の警告にOSSpinLock
置き換えられましたos_unfair_lock
。
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以降を使用している場合はクラス定数アプローチを使用し、以前のバージョンをサポートする必要がある場合はネストされた構造体アプローチを使用します。Swiftのシングルトンパターンの調査。以下のすべてのアプローチは、遅延初期化とスレッドセーフをサポートします。dispatch_onceアプローチはSwift3.0では機能しません
アプローチ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!
}
}