paint-brush
So verwenden Sie RunLoop in IOS-Anwendungenvon@alekseimarinin
901 Lesungen
901 Lesungen

So verwenden Sie RunLoop in IOS-Anwendungen

von amarinin5m2024/03/04
Read on Terminal Reader

Zu lang; Lesen

Runloop ist eine Schleife, die den Empfang und die Verarbeitung eingehender Ereignisse in einem bestimmten Thread koordiniert. Standardmäßig wird der Haupt-RunLoop immer in der Anwendung ausgeführt. Es verarbeitet Nachrichten vom System und übermittelt sie an die Anwendung. Hilfsthreads erfordern eine Selbstbestimmung über die Notwendigkeit einer RunLoop, und Sie müssen sie selbst konfigurieren und ausführen.
featured image - So verwenden Sie RunLoop in IOS-Anwendungen
amarinin HackerNoon profile picture

Was ist ein Runloop?

Eine RunLoop ist eine Schleife, die den Empfang und die Verarbeitung eingehender Ereignisse in einem bestimmten Thread koordiniert.


Der RunLoop ist in jedem Thread vorhanden, befindet sich jedoch standardmäßig im Standby-Modus und führt keine Arbeit aus.


Der Entwickler kann es bei Bedarf ausführen, es funktioniert jedoch nicht automatisch. Dazu müssen Sie Code schreiben.

Welches Problem löst Runloop?

Zunächst einmal ist RunLoop darauf ausgelegt, den Fluss eingehender Aufgaben zu verwalten und sie zum richtigen Zeitpunkt auszuführen.


Dies macht sich am deutlichsten beim Arbeiten mit der Benutzeroberfläche bemerkbar, beispielsweise bei der Verwendung von UIScrollView.


Standardmäßig wird der Haupt-RunLoop immer in der Anwendung ausgeführt. Es verarbeitet Nachrichten vom System und übermittelt sie an die Anwendung. Ein Beispiel für solche Nachrichten kann beispielsweise ein Ereignis sein, wenn ein Benutzer auf den Bildschirm klickt.


Hilfsthreads erfordern eine Selbstbestimmung über die Notwendigkeit einer RunLoop. Wenn Sie es benötigen, müssen Sie es selbst konfigurieren und ausführen. Das standardmäßige Ausführen von RunLoop wird nicht empfohlen, es ist nur in Fällen erforderlich, in denen eine aktive Interaktion mit Threads erforderlich ist.


Außerdem werden alle Timer in der Anwendung auf dem Runloop ausgeführt. Wenn Sie also in Ihrer Anwendung mit ihnen interagieren müssen, müssen Sie sich unbedingt mit den Funktionen des Runloops vertraut machen.

Wie funktioniert es?

RunLoop ist eine Schleife und verfügt über mehrere Betriebsmodi, die dem Entwickler helfen, zu verstehen, wann eine bestimmte Aufgabe ausgeführt werden muss.


RunLoop kann sich also in den folgenden Modi befinden:

  1. Default – Der Standardmodus, der Stream ist kostenlos und große Vorgänge können darin sicher ausgeführt werden.


  2. Tracking – Der Thread ist mit wichtigen Arbeiten beschäftigt. Zu diesem Zeitpunkt ist es besser, keine Aufgaben auszuführen oder zumindest einige kleine Aufgaben auszuführen.


  3. Initialization – Dieser Modus wird einmal während der Initialisierung des Streams ausgeführt.


  4. EventReceive – Dies ist ein interner Modus zum Empfangen von Systemereignissen, der normalerweise nicht verwendet wird.

  5. Common – Ist ein Platzhaltermodus, der keine praktische Bedeutung hat.


    Beim Haupt-RunLoop werden diese Modi automatisch umgeschaltet; Der Entwickler kann damit zeitaufwändige Aufgaben ausführen, sodass der Benutzer nicht bemerkt, dass die Schnittstelle hängen bleibt. Schauen wir uns ein Beispiel an.


Die Ausführungszyklusverwaltung in anderen RunLoops erfolgt nicht vollständig automatisch. Sie müssen Code für einen Thread schreiben, der den Ausführungszyklus zu einem geeigneten Zeitpunkt startet. Außerdem müssen Sie angemessen auf Ereignisse reagieren und Endlosschleifen verwenden, um sicherzustellen, dass der Ausführungszyklus nicht stoppt.


Wir haben eine UIScrollView und müssen eine große Aufgabe im Hauptthread ausführen, damit der Benutzer nichts bemerkt.


Wir können die Aufgabe wie gewohnt erledigen:


 DispatchQueue.main.async { sleep(2) self.tableView.refreshControl?.endRefreshing() }


Aber das Ergebnis wird ziemlich schlecht sein. Der Benutzer wird erhebliche Verzögerungen bei der Anwendung bemerken.

Dieser negative Effekt entsteht dadurch, dass wir eine Aufgabe im Hauptthread ausführen, ohne darauf zu achten, was gerade darin passiert.


Aus diesem Grund beginnen wir mit der Ausführung unserer großen Aufgabe in dem Moment, in dem der Benutzer mit der Schnittstelle interagiert. Dies führt natürlich dazu, dass der Benutzer sieht, dass die Schnittstelle hängt.


Dies kann durch die Verwendung des RunLoop-Mechanismus vermieden werden. Lassen Sie uns die gleiche Logik wie folgt implementieren:


 CFRunLoopPerformBlock(CFRunLoopGetMain(), CFRunLoopMode.defaultMode.rawValue) { sleep(2) self.tableView.refreshControl?.endRefreshing() }


Lassen Sie mich erklären, was hier passiert. Die CFRunLoopPerformBlock-Funktion fügt Code für die Ausführung durch RunLoop hinzu. Zusätzlich zum Codeblock selbst verfügt diese Funktion über zwei wichtige Parameter.


Der erste ist für die Auswahl verantwortlich, welcher RunLoop die Funktion ausführen soll. In diesem Beispiel wird „main“ verwendet.


Der zweite ist für den Modus verantwortlich, in dem die Aufgabe erledigt wird.


Insgesamt gibt es drei mögliche Modi:

  • Tracking – wenn der Benutzer mit der Schnittstelle interagiert, z. B. durch Scrollen durch eine UIScrollView.


  • Standard: Der Benutzer interagiert nicht mit der Schnittstelle. Zu diesem Zeitpunkt ist es möglich, eine ressourcenintensive Aufgabe sicher abzuschließen.


  • Allgemein – kombiniert den Standardmodus und den Tracking-Modus.

    Das Ergebnis der Ausführung des Programms mit dem obigen Code ist:

Wenn der Benutzer beginnt, mit der Benutzeroberfläche (UI) zu interagieren, wechselt die Hauptlaufschleife in den „Tracking“-Modus und unterbricht vorübergehend die Verarbeitung aller anderen Ereignisse, um die reibungslose Funktion der Schnittstelle sicherzustellen. Sobald der Benutzer die Interaktion mit der Schnittstelle beendet, kehrt die Ausführungsschleife in ihren „Standard“-Modus zurück und führt die Ausführung unserer Aufgabe fort.

Timer

Neben der Benutzeroberfläche ist die Schleife auch eng mit der Funktionsweise von Timern verknüpft.


Jeder Timer in der Anwendung läuft in einer Schleife, und Sie müssen besonders vorsichtig sein, um bei der Arbeit mit ihnen keine Fehler zu machen, insbesondere wenn sie für wichtige Funktionen wie die Zahlungsabwicklung verantwortlich sind.


 Timer.scheduledTimer(withTimeInterval: 1, repeats: false) { _ in // makeSomething }


Der Timer startet standardmäßig im Standardmodus, sodass er möglicherweise nicht mehr funktioniert, wenn der Benutzer gerade durch die Tabelle scrollt. Dies liegt daran, dass sich die Schleife derzeit im Tracking-Modus befindet. Aus diesem Grund funktioniert der Code im Beispiel möglicherweise nicht ordnungsgemäß. Sie können dieses Problem beheben, indem Sie im Gleichtaktmodus einen Timer hinzufügen.


 let timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: false) { _ in // makeSomething } RunLoop.main.add(timer, forMode: .common)


Außerdem kann es sein, dass die Timer nicht zum erwarteten Zeitpunkt oder überhaupt nicht ausgelöst werden. Dies liegt daran, dass RunLoop die Timer nur zu Beginn jedes Zyklus überprüft. Wenn ein Timer ausgelöst wird, nachdem der RunLoop diese Phase durchlaufen hat, erfahren wir davon erst zu Beginn der nächsten Iteration. Gleichzeitig ist die Verzögerung umso länger, je länger die Aufgabe im RunLoop ausgeführt wird.


Um dieses Problem zu lösen, können Sie einen neuen Thread erstellen, einen RunLoop in diesem Thread starten und dann einen Timer zum Thread hinzufügen, ohne weitere Aufgaben hinzuzufügen – auf diese Weise funktioniert der Timer ordnungsgemäß.


 let thread = Thread { let timer = Timer(timeInterval: 1.0, repeats: true) { timer in // makeSomething } RunLoop.current.add(timer, forMode: .default) RunLoop.current.run() } thread.start()


Das Ergebnis

In diesem Artikel haben wir uns angesehen, was RunLoop ist und welche Probleme es in iOS-Anwendungen löst. Eine RunLoop ist eine Schleife, die den Empfang und die Verarbeitung eingehender Ereignisse innerhalb eines bestimmten Threads koordiniert und über mehrere Betriebsmodi verfügt.


Es ist besonders nützlich, wenn Sie mit der Benutzeroberfläche (UI) und Timern arbeiten, da es die Möglichkeit bietet, Aufgaben zum richtigen Zeitpunkt auszuführen, wodurch Sie ein „Aufhängen“ der Schnittstelle vermeiden und den korrekten Betrieb der Timer sicherstellen können.


Auch wenn die Arbeit mit Run Loop zusätzlichen Programmieraufwand erfordert, ist es eine lohnende Investition, die die Effizienz und Stabilität Ihrer Anwendung verbessert.