Куда dispatch_once в Swift 3?

Хорошо, поэтому я узнал о новом Swifty Dispatch API в Xcode 8. Мне весело с помощью DispatchQueue.main.async , и я просматривал модуль Dispatch в Xcode, чтобы найти все новые API.

Но я также использую dispatch_once чтобы убедиться, что такие вещи, как создание однопользовательского режима и одноразовая настройка, не выполняются более одного раза (даже в многопоточной среде) … и dispatch_once нигде не встречается в новом модуле отправки?

 static var token: dispatch_once_t = 0 func whatDoYouHear() { print("All of this has happened before, and all of it will happen again.") dispatch_once(&token) { print("Except this part.") } } 

С Swift 1.x Swift использует dispatch_once за кулисами для выполнения поточно-безопасной ленивой инициализации глобальных переменных и статических свойств.

Таким образом, static var above уже использовал dispatch_once , что делает его довольно странным (и, возможно, проблематичным использовать его снова как токен для другого dispatch_once . На самом деле нет надежного способа использования dispatch_once без такой рекурсии, поэтому они получили избавитесь от него, вместо этого используйте только встроенные функции языка:

 // global constant: SomeClass initializer gets called lazily, only on first use let foo = SomeClass() // global var, same thing happens here // even though the "initializer" is an immediately invoked closure var bar: SomeClass = { let b = SomeClass() b.someProperty = "whatever" b.doSomeStuff() return b }() // ditto for static properties in classes/structures/enums class MyClass { static let singleton = MyClass() init() { print("foo") } } 

Так что это здорово, если вы используете dispatch_once для однократной инициализации, что приводит к некоторому значению – вы можете просто сделать это значение глобальной переменной или статическим свойством, которое вы инициализируете.

Но что, если вы используете dispatch_once для выполнения работы, которая не обязательно имеет результат? Вы все еще можете сделать это с помощью глобальной переменной или статического свойства: просто введите тип переменной Void :

 let justAOneTimeThing: () = { print("Not coming back here.") }() 

И если доступ к глобальной переменной или статическому свойству для одноразовой работы просто не подходит вам – скажем, вы хотите, чтобы ваши клиенты вызывали функцию «инициализировать меня», прежде чем они будут работать с вашей библиотекой, – просто оберните это доступ в функции:

 func doTheOneTimeThing() { justAOneTimeThing } 

Подробнее см. Руководство по миграции .

Другие ответы здесь и вокруг interwebs довольно велики, но я чувствую, что этот маленький лакомый кусочек также следует упомянуть:

Самое замечательное в dispatch_once было то, насколько оптимизировано это было, по сути, nixing код после первого запуска таким образом, что я почти не понимаю, но разумно уверен, что это будет намного быстрее, чем установка и проверка (реального) глобального токена.

Хотя токена может быть разумно реализована в Swift, нужно объявить, что еще одно сохраненное логическое значение не так уж и велико. Не говоря уже о небезопасности. Как говорит док , вы должны использовать «лениво инициализированный глобальный». Да, но зачем загромождать глобальный масштаб, не так ли?

Пока кто-то не убедит меня в более эффективном методе, я склонен объявлять о своем закрытии в пределах объема, в котором я буду использовать его или достаточно близко к нему, следующим образом:

 private lazy var foo: Void = { // Do this once }() 

В основном я говорю, что «когда я читаю это, foo должен быть результатом запуска этого блока». Он ведет себя точно так же, как глобальная константа let , как раз в правильной области. И красивее. Тогда я бы назвал это везде, где захочу, читая его во что-то, что никогда не будет использовано иначе. Мне нравится Swift’s для этого. Вот так:

 _ = foo 

Эта действительно крутая причуда на самом деле была вокруг, но не очень любила. Он в основном оставляет переменную в одиночку во время выполнения, как закрытое закрытие, пока что-то не захочет увидеть его результат Void . При чтении он вызывает закрытие, отбрасывает его и сохраняет его результат в foo . Void практически не использует память, поэтому последующие чтения (т.е. _ = foo ) ничего не делают на процессоре. (Не цитируйте меня на этом, кто-нибудь, пожалуйста, проверьте сборку, чтобы быть уверенным!) Имейте как можно больше, и Swift в основном уходит, заботясь об этом после первого запуска! Потеряйте этот старый dispatch_once_t и сохраните свой код так же красиво, как когда вы впервые открыли его на Рождество!

Моя проблема заключается в том, что вы можете установить foo на что-то еще до его первого чтения, а затем ваш код никогда не будет вызываться! Следовательно, глобальная константа, которая предотвращает это. Вещь в том, что константы в classе не работают хорошо с self , поэтому не играют с переменными экземпляра … Но если серьезно, когда вы все равно устанавливаете на Void ?

Это, и вам нужно будет указать тип возврата как Void или () , иначе он все равно будет жаловаться на self . Кто?

И lazy – просто сделать переменную такой же ленивой, как и должно быть, поэтому Swift не запускает ее прямо на init() .

Довольно унылый, пока вы помните, что не пишите! :П

Пример для «dispatch_once» в Swift 3.0

Шаг 1: просто замените ниже код с помощью Singleton.swift (Singleton class)

 // Singleton Class class Singleton: NSObject { var strSample = NSString() static let sharedInstance:Singleton = { let instance = Singleton () return instance } () // MARK: Init override init() { print("My Class Initialized") // initialized with variable or property strSample = "My String" } } 

Образец изображения Singleton

Шаг 2: Вызовите Singleton из ViewController.swift

 // ViewController.swift override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. let mySingleton = Singleton.sharedInstance print(mySingleton.strSample) mySingleton.strSample = "New String" print(mySingleton.strSample) let mySingleton1 = Singleton.sharedInstance print(mySingleton1.strSample) } 

Образец образца ViewController

Выход такой

 My Class Initialized My String New String New String 

Компиляция под Xcode 8 GA Swift 3

Рекомендуемый и элегантный способ создания экземпляра classа singleton dispatch_once:

 final class TheRoot { static let shared = TheRoot() var appState : AppState = .normal ... 

Чтобы использовать его:

 if TheRoot.shared.appState == .normal { ... } 

Что делают эти линии?

final – поэтому class не может быть переопределен, расширен, он также делает код несколько быстрее для запуска, меньше ограничений.

static let shared = TheRoot () – эта строка выполняет ленивый init, и она запускается только один раз.

Это решение является streamобезопасным.

В то время как шаблон «lazy var» позволяет мне перестать заботиться о токенах отправки и, как правило, более удобен, чем dispatch_once() , мне не нравится, как он выглядит на сайте вызова:

 _ = doSomethingOnce 

Я бы ожидал, что это утверждение будет больше напоминать вызов функции (поскольку это подразумевает действие), но это выглядит не так. Кроме того, необходимость писать _ = явно отказаться от результата не нужна и раздражает.

Существует лучший способ:

 lazy var doSomethingOnce: () -> Void = { print("executed once") return {} }() 

Это делает возможным следующее:

 doSomethingOnce() 

Это может быть менее эффективным (так как он вызывает пустое закрытие вместо того, чтобы просто отбрасывать Void ), но улучшенная ясность полностью стоит для меня.

Согласно Руководству по миграции :

Бесплатная функция dispatch_once больше не доступна в Swift. В Swift вы можете использовать лениво инициализированные глобальные переменные или статические свойства и получать одинаковые гарантии безопасности и гарантированные вызовы при предоставлении dispatch_once.

Пример:

  let myGlobal = { … global contains initialization in a call to a closure … }() // using myGlobal will invoke the initialization code only the first time it is used. _ = myGlobal 

Thread-safe dispatch_once:

 private lazy var foo: Void = { objc_sync_enter(self) defer { objc_sync_exit(self) } // Do this once }() 
  • Alamofire Swift 3.0 Дополнительный аргумент в вызове
  • swift Сделайте снимок и сохраните его в библиотеке фотографий
  • круглые поездки Типы данных Swift в / из данных
  • Shuffle array swift 3
  • Как я могу конкатенировать несколько необязательных строк в swift 3.0?
  • Параметры «var» устарели и будут удалены в Swift 3
  • Держите окно всегда сверху?
  • CGRectMake, CGPointMake, CGSizeMake, CGRectZero, CGPointZero недоступен в Swift
  • Как получить время (час, минута, секунда) в Swift 3 с помощью NSDate?
  • Как инициализировать структуру из объекта json
  • Что случилось с конструктором UIView? () В Swift 3.0?
  • Давайте будем гением компьютера.