W Swift jak wywołać metodę z parametrami w głównym wątku GCD?


192

W mojej aplikacji mam funkcję, która tworzy NSRURLSession i wysyła NSURLRequest za pomocą

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

W bloku ukończenia tego zadania muszę wykonać obliczenia, które dodają obraz UIImagającego do wywołującego kontrolera widoku. Mam func o nazwie

func displayQRCode(receiveAddr, withAmountInBTC:amountBTC)

wykonuje obliczenia dodające UIImage. Jeśli spróbuję uruchomić kod dodający widok wewnątrz bloku uzupełniania, Xcode zgłasza błąd informujący, że nie mogę używać silnika układu podczas pracy w tle. Znalazłem więc kod na SO, który próbuje ustawić w kolejce metodę w głównym wątku:

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

Nie wiem jednak, jak dodać parametry „receiveAddr” i „ountBTC ”do tego wywołania funkcji. Jak mam to zrobić lub czy ktoś może zasugerować optymalny sposób dodania wywołania metody do głównej kolejki aplikacji?

Odpowiedzi:


496

Nowoczesne wersje Swift używają DispatchQueue.main.asyncdo wysyłania do głównego wątku:

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

Aby wysłać w kolejce głównej, użyj:

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

Użyto starszych wersji Swift:

dispatch_async(dispatch_get_main_queue(), {
  let delegateObj = UIApplication.sharedApplication().delegate as YourAppDelegateClass
  delegateObj.addUIImage("yourstring")
})

Chociaż masz rację, że twoja sugestia działa, myślę, że moja odpowiedź jest nieco lepsza, ponieważ nie wywołuje wywołania UIApplication.sharedApplication, co jest niezwykłe i może zrzucić innych czytelników mojego kodu. Zakres mojej odpowiedzi jest ograniczony do ważnych obiektów, a twój zawiera dodatkowe obiekty, które wymagają ode mnie przeczytania większej liczby dokumentów, aby dowiedzieć się dokładnie, co robię. Zredagowałem moje oryginalne pytanie, aby zawierało prawidłowe wywołanie funkcji. Myślałem, że kod displayQRCode nie jest wystarczająco konkretny, ale teraz z naszymi komentarzami. Dzięki za zwrócenie na to uwagi.
almel

84

Wersja Swift 3+ i Swift 4:

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

Swift 3 i Xcode 9.2:

dispatch_async_on_main_queue {
    print("Hello")
}

15

Swift 2

Przy zamykaniu końcowym staje się to:

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

Zamknięcia końcowe to szybki cukier syntaktyczny, który umożliwia zdefiniowanie zamknięcia poza zakresem parametru funkcji. Aby uzyskać więcej informacji, zobacz Trailing Closures in Swift 2.2 Programming Language Guide.

W dispatch_async przypadku API func dispatch_async(queue: dispatch_queue_t, _ block: dispatch_block_t), ponieważ dispatch_block_tjest typu alias dla () -> Void- zamknięcie, które otrzymuje 0 parametrów i nie mają wartość powrotną, i blok jest ostatnim parametrów funkcji można określić zamykający w zewnętrznym zakresu dispatch_async.


1
to dokładnie 3 linie, których szukam ... teraz możesz przestać czytać w moich myślach
Laszlo,

8

Odśwież kolekcję Zobacz w głównym wątku

DispatchQueue.main.async {
    self.collectionView.reloadData()
}

7

Oto ładniejsza (IMO) składnia w stylu Swifty / Cocoa, aby osiągnąć ten sam wynik, co inne odpowiedzi:

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

Lub możesz pobrać popularną bibliotekę Async Swift, aby uzyskać jeszcze mniej kodu i więcej funkcji:

Async.main {
    // Your code here
}

metoda zmieniono naOperationQueue.main.addOperation({ }
Frostmourne

3

Właściwym sposobem na to jest użycie dispatch_async w main_queue, tak jak zrobiłem w poniższym kodzie

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

2

Oto miła, globalna funkcja, którą możesz dodać, aby uzyskać ładniejszą składnię:

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

I wykorzystanie

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

Nie zapomnij osłabić siebie, jeśli używasz siebie wewnątrz zamknięcia.

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

1
Czy mógłbyś wyjaśnić, dlaczego powinniśmy to robić?
Jackspicer

To dlatego, że może tworzyć cykle pamięci - tj. Mam silne odniesienie do czegoś i ma silne odniesienie do mnie. Oznacza to, że żaden z nas nie może opuścić stosu pamięci.
jackofallcode
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.