В Swift как вызвать метод с параметрами в основном потоке GCD?

192

В моем приложении у меня есть функция, которая создает NSRURLSession и отправляет NSURLRequest, используя

sesh.dataTaskWithRequest(req, completionHandler: {(data, response, error)

В блоке завершения для этой задачи мне нужно выполнить некоторые вычисления, которые добавляют UIImage к вызывающему viewcontroller. У меня есть функция под названием

func displayQRCode(receiveAddr, withAmountInBTC:amountBTC)

это делает вычисление добавления UIImage. Если я пытаюсь запустить код добавления представления внутри блока завершения, XCode выдает ошибку, говорящую, что я не могу использовать механизм компоновки в фоновом процессе. Итак, я нашел код на SO, который пытается поставить метод в очередь в главном потоке:

let time = dispatch_time(DISPATCH_TIME_NOW, Int64(0.0 * Double(NSEC_PER_MSEC)))

dispatch_after(time, dispatch_get_main_queue(), {
    let returned = UIApplication.sharedApplication().sendAction("displayQRCode:", to: self.delegate, from: self, forEvent: nil)
})

Однако я не знаю, как добавить параметры "receiveAddr" и "amountBTC" к этому вызову функции. Как мне это сделать, или кто-то может предложить оптимальный способ добавления вызова метода в основную очередь приложения?

almel
источник

Ответы:

497

Современные версии Swift используют DispatchQueue.main.asyncдля отправки в основной поток:

DispatchQueue.main.async { 
  // your code here
}

Для отправки после основной очереди используйте:

DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
  // your code here
}

Старые версии Swift использовались:

dispatch_async(dispatch_get_main_queue(), {
  let delegateObj = UIApplication.sharedApplication().delegate as YourAppDelegateClass
  delegateObj.addUIImage("yourstring")
})
codester
источник
Хотя вы правы в том, что ваше предложение работает, я думаю, что мой ответ несколько лучше, потому что он не вызывает UIApplication.sharedApplication, что является необычным и может отбросить других читателей моего кода. Объем моего ответа ограничен важными объектами, а ваш вводит вспомогательные объекты, которые требуют от меня читать больше документов, чтобы точно узнать, что я делаю. И я отредактировал свой оригинальный вопрос, чтобы он содержал правильный вызов функции. Я думал, что displayQRCode не был достаточно конкретным, но теперь с нашими комментариями. Спасибо что подметил это.
Almel
84

Версия Swift 3+ и Swift 4:

DispatchQueue.main.async {
    print("Hello")
}

Swift 3 и Xcode 9.2:

dispatch_async_on_main_queue {
    print("Hello")
}
DazChong
источник
15

Swift 2

Используя замыкающие замыкания это становится:

dispatch_async(dispatch_get_main_queue()) {
    self.tableView.reloadData()
}

Trailing Closures - это синтаксический сахар Swift, который позволяет определять замыкание вне области действия параметра функции. Для получения дополнительной информации см. Trailing Closures в Руководстве по языку программирования Swift 2.2.

В случае dispatch_async API , это func dispatch_async(queue: dispatch_queue_t, _ block: dispatch_block_t)так dispatch_block_tэто псевдоним типа для () -> Void- A закрытия , которая получает параметры 0 и не имеет возвращаемое значения, и блока является последним параметром функции мы можем определить замыкание во внешней сфере dispatch_async.

Максим Векслер
источник
1
это были именно те 3 строки, которые я ищу ... теперь вы можете перестать читать мои мысли
Ласло
8

Перезагрузить коллекциюПосмотреть на главной теме

DispatchQueue.main.async {
    self.collectionView.reloadData()
}
Санджай Мали
источник
7

Вот более приятный (IMO) синтаксис стиля Swifty / Cocoa для достижения того же результата, что и в других ответах:

NSOperationQueue.mainQueue().addOperationWithBlock({
    // Your code here
})

Или вы можете воспользоваться популярной библиотекой Async Swift для еще меньшего количества кода и большей функциональности:

Async.main {
    // Your code here
}
pejalo
источник
метод переименованOperationQueue.main.addOperation({ }
Ледяная Скорбь
3

Правильный способ сделать это - использовать dispatch_async в main_queue, как я делал в следующем коде

dispatch_async(dispatch_get_main_queue(), {
    (self.delegate as TBGQRCodeViewController).displayQRCode(receiveAddr, withAmountInBTC:amountBTC)
})
almel
источник
2

Вот хорошая маленькая глобальная функция, которую вы можете добавить для лучшего синтаксиса:

func dispatch_on_main(block: dispatch_block_t) {
    dispatch_async(dispatch_get_main_queue(), block)
}

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

dispatch_on_main {
    // Do some UI stuff
}
Матеуш Гжегожек
источник
2
//Perform some task and update UI immediately.
DispatchQueue.global(qos: .userInitiated).async {  
    // Call your function here
    DispatchQueue.main.async {  
        // Update UI
        self.tableView.reloadData()  
    }
}

//To call or execute function after some time
DispatchQueue.main.asyncAfter(deadline: .now() + 5.0) {
    //Here call your function
}

//If you want to do changes in UI use this
DispatchQueue.main.async(execute: {
    //Update UI
    self.tableView.reloadData()
})
IOS
источник
2

Не забудьте ослабить себя, если вы используете себя внутри замыкания.

dispatch_async(dispatch_get_main_queue(),{ [weak self] () -> () in
    if let strongSelf = self {
        self?.doSomething()
    }
})
villy393
источник
1
Не могли бы вы объяснить, почему мы должны это делать?
Джекспайсер
Это потому, что он может создавать циклы памяти - то есть, у меня есть сильная ссылка на что-то, и это имеет сильную ссылку на меня. То есть ни один из нас не может оставить кучу памяти.
jackofallcode