Sobes.tech
Middle+

Kiedy należy używać pętli run w rozwoju iOS?

sobes.tech AI

Odpowiedź od AI

Pętla zdarzeń (Run loop) powinna być używana, gdy konieczne jest zarządzanie zachowaniem wątku, pozwalając mu oczekiwać na zdarzenia i wykonywać odpowiedni kod, zamiast kończyć się natychmiast po wykonaniu zadania. Jest to krytyczne dla:

  1. Głównego wątku (Main Thread): Pętla zdarzeń głównego wątku obsługuje interakcję z UIKit/AppKit, zdarzenia użytkownika (dotknięcia, kliknięcia), timery, zdarzenia sieciowe i wywołania performSelector:withObject:afterDelay:. Bez niej aplikacja nie będzie mogła reagować na działania użytkownika ani aktualizować UI.

  2. Wątków podrzędnych: Gdy wątek w tle musi wykonywać zadania asynchronicznie, nasłuchiwać nadchodzących danych (np. gniazda), przetwarzać wiadomości z innych wątków lub używać timerów. Typowe scenariusze obejmują:

    • Pracę z gniazdami.
    • Wykonywanie powtarzalnych zadań za pomocą timerów.
    • Odbieranie i przetwarzanie wiadomości IPC (Komunikacja międzyprocesowa).
    • Utrzymanie aktywności wątku do obsługi przyszłych żądań bez konieczności ciągłego tworzenia i niszczenia.

Przykładem użycia pętli zdarzeń w wątku podrzędnym może być utrzymanie połączenia sieciowego:

// Przykład w Swift
class NetworkManager {
    private let networkThread = Thread {
        // Kod inicjalizacji sesji sieciowej, gniazd itp.

        // Dodanie źródła zdarzeń (np. gniazdo lub port) do pętli zdarzeń
        // CFRunLoopAddSource(CFRunLoopGetCurrent(), source, CFRunLoopMode.defaultMode)

        // Dodanie timera, jeśli konieczne
        // CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, CFRunLoopMode.defaultMode)

        // Uruchomienie pętli zdarzeń. Zablokuje wątek, aż zdarzenie zostanie odebrane.
        // .distantFuture - aby pętla nie kończyła się od razu
        RunLoop.current.run()

        // Kod sprzątający po zakończeniu pętli
        print("Wątek sieciowy kończy")
    }

    func start() {
        // Użycie `start()` zamiast `detachNewThreadSelector`
        // bardziej nowoczesne podejście
        networkThread.start()
    }

    // Metoda do zatrzymania pętli zdarzeń z innego wątku
    func stop() {
        // Sygnalizacja pętli zdarzeń o konieczności zakończenia
        // CFRunLoopStop(CFRunLoopGetCurrent()) // lub dodanie specjalnego zdarzenia do obsługi
        // Pętla zdarzeń zwykle kończy się, gdy nie ma już źródeł zdarzeń lub po wywołaniu stop
    }
}

Run loop nie jest potrzebny dla wątków, które wykonują jedno zadanie i kończą (np. jednorazowe operacje w tle za pomocą DispatchQueue.global().async). W takich przypadkach wystarczy użyć Grand Central Dispatch (GCD).