Tak, jest różnica. Główną kolejką wywołania jest kolejka szeregowa. Oznacza to, że podczas wykonywania zadania, które zostało mu przekazane, nie może wykonywać żadnych innych zadań. To prawda, nawet jeśli uruchamia wewnętrzną pętlę zdarzeń.
-performSelectorOnMainThread:...
działa poprzez źródło pętli rozruchowej. Źródła działające w pętli mogą uruchamiać pętlę wewnątrz pętli, nawet jeśli ta wewnętrzna pętla uruchomieniowa jest wynikiem wcześniejszego uruchomienia tego samego źródła.
Jeden przypadek, w którym ten bit mnie polega na uruchomieniu otwartego okna dialogowego pliku modalnego. (Bez piaskownicy, więc okno dialogowe jest w trakcie.) Zainicjowałem dialog modalny z zadania przesłanego do głównej kolejki wysyłkowej. Okazuje się, że wewnętrzna implementacja otwartego okna dialogowego również asynchronicznie przekazuje niektóre prace do głównej kolejki. Ponieważ główna kolejka wywołania była zajęta przez moje zadanie, które uruchamiało to okno dialogowe, nie przetwarzała zadań tego frameworku aż do zakończenia okna dialogowego. Symptomem było to, że okno dialogowe nie wyświetliło plików, dopóki nie upłynął określony czas oczekiwania, który trwał mniej więcej minutę.
Należy zauważyć, że nie był to przypadek zakleszczenia spowodowanego przez synchroniczne żądaniez główną wątkiem, chociaż może się to również zdarzyć. W przypadku GCD takie synchroniczne żądanie jest pewnie zakleszczone. Z -performSelectorOnMainThread:...
, nie będzie, ponieważ żądanie synchroniczne (waitUntilDone
ustawione na YES
) jest uruchamiane bezpośrednio.
Przy okazji, mówisz "pierwszy kod jest asynchroniczny", jakby chciał kontrastować z drugim kodem. Oba są asynchroniczne, ponieważ minąłeś NO
dla waitUntilDone
w drugim.
Aktualizacja:
Rozważmy kod tak:
dispatch_async(dispatch_get_main_queue(), ^{
printf("outer task, milestone 1\n");
dispatch_async(dispatch_get_main_queue(), ^{
printf("inner task\n");
});
// Although running the run loop directly like this is uncommon, this simulates what
// happens if you do something like run a modal dialog or call -[NSTask waitUntilExit].
[[NSRunLoop mainRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]];
printf("outer task, milestone 2\n");
});
Będzie to log:
outer task, milestone 1
outer task, milestone 2
inner task
Wewnętrzna zadanie nie będzie biec aż zewnętrzna zadanie ma zakończony. To prawda, nawet jeśli zewnętrzne zadanie uruchomiło pętlę głównego uruchomienia, która jest tym, co przetwarza zadania wysłane do głównej kolejki. Powodem jest to, że główna kolejka jest kolejką szeregową i nigdy nie uruchomi nowego zadania, dopóki nadal będzie wykonywać zadanie.
Jeśli zmienisz wewnętrzny wewnętrzny dispatch_async()
na dispatch_sync()
, program zostanie zablokowany.
Natomiast rozważenia:
- (void) task2
{
printf("task2\n");
}
- (void) task1
{
printf("task1 milestone 1\n");
[self performSelectorOnMainThread:@selector(task2) withObject:nil waitUntilDone:NO];
[[NSRunLoop mainRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]];
printf("task1 milestone 2\n");
}
(... in some other method:)
[self performSelectorOnMainThread:@selector(task1) withObject:nil waitUntilDone:NO];
To będzie log:
task1 milestone 1
task2
task1 milestone 2
Running pętlę run wewnątrz od -task1
daje możliwość wewnętrznej -performSelectorOnMainThread:...
do uruchomienia. To duża różnica między tymi dwoma technikami.
Jeśli zmienisz NO
na YES
w -task1
, nadal działa bez impasu. To kolejna różnica. Dzieje się tak, ponieważ gdy -performSelectorOnMainThread:...
jest wywoływana z waitUntilDone
ustawiona na wartość true, sprawdza, czy jest wywoływana w głównym wątku. Jeśli tak, to po prostu bezpośrednio wywołuje wybierak właśnie tam. To tak, jakby to był tylko telefon do -performSelector:withObject:
.
* -performSelectorOnMainThread: ...działa poprzez źródło pętli rozruchowej. Źródła działające w pętli mogą uruchamiać pętlę wewnątrz pętli, nawet jeśli ta wewnętrzna pętla jest wynikiem wcześniejszego uruchomienia tego samego źródła. * Whaaaaaat? Nie można zrozumieć tego zwrotu. – SpaceDog
To nie jest niemożliwe, wystarczy trochę zrozumieć o pętlach uruchamiania i źródłach pętli. Zobacz https://developer.apple.com/library/ios/documentation/cocoa/Conceptual/Multithreading/RunLoopManagement/RunLoopManagement.html –
Zaktualizowałem moją odpowiedź przykładowym kodem. –