Як у Swift як викликати метод з параметрами на головному потоці GCD?


192

У своєму додатку у мене є функція, яка створює NSRURLSession і надсилає NSURLRequest за допомогою

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

У блоці завершення цього завдання мені потрібно зробити кілька обчислень, які додають UIImage до виклику контролера перегляду. У мене функціонер називається

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

Однак я не знаю, як додати параметри "receAddr" і "iznosBTC" до цього виклику функції. Як би це зробити, чи хтось може запропонувати оптимальний спосіб додавання виклику методу до головної черги програми?

Відповіді:


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

Хоча ви впевнені, що ваша пропозиція працює, я вважаю, що моя відповідь трохи краща, тому що вона не робить дзвінок на 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")
}

15

Швидкий 2

Використовуючи задні закриття, це стає:

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

Трейлінг замикань - це синтаксичний цукор Swift, який дозволяє визначити закриття поза межами області параметра функції. Для отримання додаткової інформації див. Попередні закриття в Посібнику з мови програмування 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 рядки я шукаю ... тепер ви можете перестати читати мій погляд
Laszlo


7

Ось приємний синтаксис стилю Swifty / Cocoa (IMO) для досягнення того ж результату, що й інші відповіді:

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

Або ви можете захопити популярну бібліотеку Async Swift для ще менше коду та більшої функціональності:

Async.main {
    // Your code here
}

метод перейменований наOperationQueue.main.addOperation({ }
Frostmourne

3

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

dispatch_async(dispatch_get_main_queue(), {
    (self.delegate as TBGQRCodeViewController).displayQRCode(receiveAddr, withAmountInBTC:amountBTC)
})

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

2

Не забувайте схиляти себе, якщо ви використовуєте себе у закритті.

dispatch_async(dispatch_get_main_queue(),{ [weak self] () -> () in
    if let strongSelf = self {
        self?.doSomething()
    }
})

1
Чи можете ви поясніть, чому ми маємо це робити?
Джекшпікер

Це тому, що він може створювати цикли пам'яті - тобто я сильно посилаюся на щось, і воно має чітке посилання на мене. Це означає, що ніхто з нас не може залишити купу пам'яті.
jackofallcode
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.