dispatch_once после изменения API Swift 3 GCD

Каков новый синтаксис dispatch_once в Swift после изменений, внесенных в языковой версии 3? Старая версия была следующей.

 var token: dispatch_once_t = 0 func test() { dispatch_once(&token) { } } 

Это изменения в libdispatch, которые были сделаны.

Из документа :

отправка
Бесплатная функция 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. 

Хотя использование ленивых инициализированных глобалов может иметь смысл для какой-то однократной инициализации, это не имеет смысла для других типов. Очень полезно использовать ленивые инициализированные глобалы для таких вещей, как одиночные игры, это не имеет большого смысла для таких вещей, как защита настройки 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. 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!" ) } 

Поскольку мы в настоящее время находимся в переходном периоде от 2 до 3, вот пример быстрой реализации 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. 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() } } 

Развернувшись на ответе Тода Каннингема выше, я добавил еще один метод, который делает маркер автоматически из файла, функции и строки.

 public extension DispatchQueue { private static var _onceTracker = [String]() public class func once(file: String = #file, function: String = #function, line: Int = #line, block:(Void)->Void) { let token = file + ":" + function + ":" + String(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. or a GUID - parameter block: Block to execute once */ public class func once(token: String, block:(Void)->Void) { objc_sync_enter(self) defer { objc_sync_exit(self) } if _onceTracker.contains(token) { return } _onceTracker.append(token) block() } } 

Так что проще назвать:

 DispatchQueue.once { setupUI() } 

и вы все равно можете указать токен, если хотите:

 DispatchQueue.once(token: "com.hostname.project") { setupUI() } 

Я предполагаю, что вы можете получить столкновение, если у вас есть тот же файл в двух модулях. Жаль, что нет #module

редактировать

@ Ответ Фризлаба – это решение не гарантирует streamобезопасность. Следует использовать альтернативу, если это имеет решающее значение.

Простым решением является

 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 предоставляет.

Swift 3: для тех, кто любит многоразовые classы (или структуры):

 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 из-за предупреждений об 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) } } 

Можно объявить как

 private lazy var doOnce: ()->() = { /* do some work only once per instance */ return {} }() 

затем позвоните

 doOnce() 

Используйте подход константы classа, если вы используете Swift 1.2 или выше и вложенный структурный подход, если вам нужно поддерживать более ранние версии. Исследование модели Синглтона в Свифте. Все подходы ниже поддерживают ленивую инициализацию и безопасность streamов. Метод 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: class dispatch_once 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! } 

}

  I have created below function func executeOnce(code: @escaping () -> Void) { if UserDefaults.standard.value(forKey: "3333##112233") == nil { code() UserDefaults.standard.setValue("vv", forKey: "3333##112233") UserDefaults.standard.synchronize() } } 

И использовать как ниже

  executeOnce { print("onces") } 
Давайте будем гением компьютера.