отправка один раз после изменения API Swift 3 GCD


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

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

эти изменения в libdispatch это было сделано.

9 70

9 ответов:

С doc:

Dispatch
Бесплатная функция dispatch_once больше не доступна в Быстрый. В 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.

вот реализация стиля Swift 3 dispatch_once:

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()
    }

}

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

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.<name> 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

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

@Frizlab - это решение не гарантируется, чтобы быть потокобезопасными. Альтернатива должна быть использована, если это имеет решающее значение

простое решение

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: Для тех, кто любит многоразовые классы (или структуры):

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)
   }
}

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

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

затем вызов

doOnce()

используйте подход константы класса, если вы используете Swift 1.2 или выше, и подход вложенной структуры, если вам нужно поддерживать более ранние версии. Исследование паттерн Singleton в Swift. Все приведенные ниже подходы поддерживают ленивую инициализацию и потокобезопасность. dispatch_once подход не работает в swift 3.0

подход a: константа класса класс Синглетона {

static let sharedInstance = SingletonA()

init() {
    println("AAA");
}

} Подход B: вложенная структура класс SingletonB {

class var sharedInstance: SingletonB {
    struct Static {
        static let instance: SingletonB = SingletonB()
    }
    return Static.instance
}

} Подход С: 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")
    }