Каков новый синтаксис dispatch_once
Swift после изменений, внесенных в языковую версию 3? Старая версия была следующей.
var token: dispatch_once_t = 0
func test() {
dispatch_once(&token) {
}
}
Каков новый синтаксис dispatch_once
Swift после изменений, внесенных в языковую версию 3? Старая версия была следующей.
var token: dispatch_once_t = 0
func test() {
dispatch_once(&token) {
}
}
pod 'SwiftDispatchOnce', '~> 1.0'
Ура. :]Ответы:
Из документа :
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
было ясно. Это, к сожалению, некрасиво и сбивает с толку ..Хотя использование ленивых инициализированных глобальных объектов может иметь смысл для некоторой однократной инициализации, это не имеет смысла для других типов. Имеет большой смысл использовать ленивые инициализированные глобальные переменные для таких вещей, как синглтоны, это не имеет большого смысла для таких вещей, как защита настройки swizzle.
Вот реализация dispatch_once в стиле Swift 3:
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
больше.Расширяя ответ Тода Каннингема выше, я добавил еще один метод, который автоматически делает токен из файла, функции и строки.
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
источник
редактировать
Ответ @Frizlab - это решение не гарантирует многопоточность. Если это важно, следует использовать альтернативу.
Простое решение
lazy var dispatchOnce : Void = { // or anyName I choose self.title = "Hello Lazy Guy" return }()
используется как
override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() _ = dispatchOnce }
источник
Вы все еще можете использовать его, если добавите заголовок моста:
Затем
.m
где-нибудь:Теперь вы можете использовать
mxcl_dispatch_once
Swift.В основном вам следует использовать то, что предлагает Apple, но у меня было несколько законных применений, когда мне нужно было
dispatch_once
с одним токеном для двух функций, и это не покрывается тем, что Apple предоставляет взамен.источник
Вы можете объявить функцию переменной верхнего уровня следующим образом:
private var doOnce: ()->() = { /* do some work only once per instance */ return {} }()
затем вызовите это где угодно:
источник
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 // ... } } }
Обновление (28 апреля 2017 г.):
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 или выше, и подход вложенной структуры, если вам нужно поддерживать более ранние версии. Исследование паттерна Синглтон в Swift. Все приведенные ниже подходы поддерживают отложенную инициализацию и безопасность потоков. Подход dispatch_once не работает в Swift 3.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! } }
источник