Wywołanie metody w głównym wątku?

83

Przede wszystkim piszę kod na iPhone'a. Muszę mieć możliwość wywołania metody w głównym wątku bez użycia performSelectorOnMainThread. Powodem, którego nie chcę używać, performSelectorOnMainThreadjest to, że powoduje to problem, gdy próbuję utworzyć makietę do testów jednostkowych.

[self performSelectorOnMainThread:@Selector(doSomething) withObject:nil];

Problem polega na tym, że mój makieta wie, jak dzwonić, doSomethingale nie wie, jak dzwonić performSelectorOnMainThread.

Więc jakieś rozwiązanie?

aryaxt
źródło

Odpowiedzi:

274

Cel C

dispatch_async(dispatch_get_main_queue(), ^{
    [self doSomething];
});

Szybki

DispatchQueue.main.async {
    self.doSomething()
}

Legacy Swift

dispatch_async(dispatch_get_main_queue()) {
    self.doSomething()
}
aryaxt
źródło
Udało Ci się właśnie zrobić mój dzień dzięki szybkiemu kodowi 3. Dzięki!
Felipe Balduino
Dobrą praktyką jest nie używać siebie bezpośrednio do bloku. zamiast tego użyj słabego odniesienia do niego.
Jageen
2

W oprogramowaniu jest takie powiedzenie, że dodanie warstwy pośredniej naprawi prawie wszystko.

Niech metoda doSomething będzie powłoką pośrednią, która wykonuje tylko performSelectorOnMainThread w celu wywołania metody really_doSomething w celu wykonania rzeczywistej pracy Something. Lub, jeśli nie chcesz zmieniać metody doSomething, poproś jednostkę testową o wywołanie metody doSomething_redirect_shell, aby zrobić coś podobnego.

hotpaw2
źródło
1

Oto lepszy sposób na zrobienie tego w Swift:

runThisInMainThread { () -> Void in
    // Run your code
    self.doSomething()
}

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

Jest zawarta jako standardowa funkcja w moim repozytorium, sprawdź: https://github.com/goktugyil/EZSwiftExtensions

Esqarrouth
źródło
Nie jest to w żaden sposób lepsze, stworzyłeś funkcję, która nic nie robi, tylko wywołuje inną. tak przy okazji, że szybką składnię można jeszcze bardziej uprościć "() -> Void in" nie jest potrzebne
aryaxt
Jest bardziej czytelny / zapisywalny dla człowieka. Tak, autouzupełnianie dodaje „() -> Void in”. Czy w ogóle można wyłączyć to zachowanie autouzupełniania w void> void closures?
Esqarrouth
nazwa metody, którą masz, może być myląca, wygląda na to, że blok zostałby wykonany od razu w głównym wątku, ale to nieprawda. dispatch_async w głównej kolejce dodaje blok kodu do następnego runloop, to ważne zachowanie jest ukryte za metodą o nazwie „runThisInMainThread
aryaxt
1
jest to oczekiwane zachowanie, dispatch_async dodaje kod na końcu kolejki. Jeśli chcesz, aby został wywołany od razu, powinieneś zamiast tego zrobić dispatch_sync. Jeśli wykonasz dispatch_sync w kolejce dla wątku, w którym już jesteś, spowoduje to blokadę wątku. w twoim przykładzie kolejność wydruków to „a”, „c”, „b”. a i c są wykonywane w 1 runloop, ponieważ są w tym samym zakresie. b jest dodawany na koniec kolejki, więc czasami jest wywoływany później, gdy inne istniejące pozycje w kolejce zostaną ukończone
aryaxt
1
@Esqarrouth - czy NA PEWNO masz dispatch_asynczablokowany kod po wywołaniu go? Cały sens używania asynczamiast syncNIE blokować tego, co następuje. (Oczywiście blockkod BĘDZIE zablokował wszystko inne w głównym wątku , ponieważ celem żądanego kodu jest wykonanie w głównym wątku. Jeśli chcesz uruchomić kod w tle, poprosiłbyś o inną kolejkę, nie dispatch_get_main_queue)
ToolmakerSteve
1

A teraz w Swift 3:

DispatchQueue.main.async{
   self.doSomething()
}
RomOne
źródło
-4
// Draw Line
    func drawPath(from polyStr: String){
        DispatchQueue.main.async {
            let path = GMSPath(fromEncodedPath: polyStr)
            let polyline = GMSPolyline(path: path)
            polyline.strokeWidth = 3.0
            polyline.strokeColor = #colorLiteral(red: 0.05098039216, green: 0.5764705882, blue: 0.2784313725, alpha: 1)
            polyline.map = self.mapVu // Google MapView
        }

    }
Muhammad Zeeshan
źródło