Jaka jest nowa składnia dispatch_once
w Swift po zmianach wprowadzonych w wersji językowej 3? Stara wersja wyglądała następująco.
var token: dispatch_once_t = 0
func test() {
dispatch_once(&token) {
}
}
Jaka jest nowa składnia dispatch_once
w Swift po zmianach wprowadzonych w wersji językowej 3? Stara wersja wyglądała następująco.
var token: dispatch_once_t = 0
func test() {
dispatch_once(&token) {
}
}
pod 'SwiftDispatchOnce', '~> 1.0'
Pozdrawiam. :]
Odpowiedzi:
Z dokumentu :
Wysyłka
Bezpłatna funkcja dispatch_once nie jest już dostępna w Swift. W Swift możesz leniwie inicjować globale lub statyczne właściwości i uzyskać takie same gwarancje bezpieczeństwa wątków i jednorazowego wywołania, jak podano w dispatch_once. Przykład:
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
był czysty. To niestety jest brzydkie i zagmatwane…
Chociaż używanie leniwych zainicjowanych globali może mieć sens w przypadku jednorazowej inicjalizacji, nie ma sensu w przypadku innych typów. Używanie leniwych zainicjowanych globali do rzeczy takich jak singletony ma sens, nie ma to większego sensu w przypadku rzeczy takich jak ochrona konfiguracji swizzle.
Oto implementacja metody dispatch_once w stylu 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()
}
}
Oto przykład użycia:
DispatchQueue.once(token: "com.vectorform.test") {
print( "Do This Once!" )
}
lub używając UUID
private let _onceToken = NSUUID().uuidString
DispatchQueue.once(token: _onceToken) {
print( "Do This Once!" )
}
Ponieważ jesteśmy obecnie w okresie przechodzenia od swift 2 do 3, oto przykład implementacji 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
i objc_sync_exit
więcej.
Rozszerzając powyższą odpowiedź Toda Cunninghama, dodałem inną metodę, która automatycznie tworzy token z pliku, funkcji i wiersza.
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()
}
}
Więc może być prościej zadzwonić:
DispatchQueue.once {
setupUI()
}
i nadal możesz określić token, jeśli chcesz:
DispatchQueue.once(token: "com.hostname.project") {
setupUI()
}
Przypuszczam, że możesz mieć kolizję, jeśli masz ten sam plik w dwóch modułach. Szkoda, że nie ma#module
Edytować
Proste rozwiązanie to
lazy var dispatchOnce : Void = { // or anyName I choose
self.title = "Hello Lazy Guy"
return
}()
używany jak
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
_ = dispatchOnce
}
Nadal możesz go używać, jeśli dodasz nagłówek mostkujący:
typedef dispatch_once_t mxcl_dispatch_once_t;
void mxcl_dispatch_once(mxcl_dispatch_once_t *predicate, dispatch_block_t block);
Następnie .m
gdzieś:
void mxcl_dispatch_once(mxcl_dispatch_once_t *predicate, dispatch_block_t block) {
dispatch_once(predicate, block);
}
Powinieneś teraz móc używać mxcl_dispatch_once
z poziomu Swift.
Przede wszystkim powinieneś użyć tego, co sugeruje Apple, ale miałem kilka uzasadnionych zastosowań, w których musiałem, dispatch_once
z jednym tokenem w dwóch funkcjach, a zamiast tego nie jest to objęte tym, co zapewnia Apple.
Możesz zadeklarować funkcję zmiennej najwyższego poziomu w następujący sposób:
private var doOnce: ()->() = {
/* do some work only once per instance */
return {}
}()
następnie zadzwoń do tego gdziekolwiek:
doOnce()
Swift 3: Dla tych, którzy lubią klasy (lub struktury) wielokrotnego użytku:
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)
}
}
Stosowanie:
class MyViewController: UIViewController {
private let /* var */ setUpOnce = DispatchOnce()
override func viewWillAppear() {
super.viewWillAppear()
setUpOnce.perform {
// Do some work here
// ...
}
}
}
Aktualizacja (28 kwietnia 2017 r.): OSSpinLock
Zastąpiono os_unfair_lock
ostrzeżeniami o stosownym wycofaniu w zestawie 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
zastąpiony os_unfair_lock
. BTW: Oto dobry film WWDC o Concurrent Programming
: developer.apple.com/videos/play/wwdc2016/720
Poprawiam powyższe odpowiedzi otrzymuję wynik:
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()
}
}
}
Użyj metody stałej klasy, jeśli używasz Swift 1.2 lub nowszego, a zagnieżdżonej struktury, jeśli potrzebujesz obsługiwać wcześniejsze wersje. Eksploracja wzoru Singleton w Swift. Wszystkie poniższe podejścia obsługują leniwą inicjalizację i bezpieczeństwo wątków. Podejście dispatch_once nie działa w Swift 3.0
Podejście A: stała klasy
class SingletonA {
static let sharedInstance = SingletonA()
init() {
println("AAA");
}
}
Podejście B: struktura zagnieżdżona
class SingletonB {
class var sharedInstance: SingletonB {
struct Static {
static let instance: SingletonB = SingletonB()
}
return Static.instance
}
}
Podejście 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!
}
}