Каков новый синтаксис 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'
Ура. :]
Ответы:
Из документа :
Dispatch
Бесплатная функция 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
было ясно. Это, к сожалению, некрасиво и сбивает с толку ..
Хотя использование ленивых инициализированных глобальных объектов может иметь смысл для некоторой однократной инициализации, это не имеет смысла для других типов. Имеет большой смысл использовать ленивые инициализированные глобальные переменные для таких вещей, как синглтоны, это не имеет большого смысла для таких вещей, как защита настройки 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
редактировать
Простое решение
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()
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!
}
}