Arbeiten mit der NSOperationQueue-Klasse

Multitasking verhindert das Einfrieren von Apps. In den meisten Programmiersprachen ist dies ein bisschen schwierig, aber die Klasse NSOperationQueue in iOS macht es einfach!

Dieses Tutorial zeigt, wie Sie das verwenden NSOperationQueue Klasse. Ein NSOperationQueue-Objekt ist eine Warteschlange, die Objekte der NSOperation Klassentyp. Ein NSOperation-Objekt, einfach ausgedrückt, stellt eine einzelne Aufgabe dar, die sowohl die Daten als auch den Code enthält, der sich auf die Aufgabe bezieht. Die NSOperationQueue behandelt und verwaltet die Ausführung aller NSOperation-Objekte (die Tasks), die ihr hinzugefügt wurden. Die Ausführung erfolgt mit dem Hauptthread der Anwendung. Wenn ein NSOperation-Objekt zur Warteschlange hinzugefügt wird, wird es sofort ausgeführt und verlässt die Warteschlange nicht, bis es abgeschlossen ist. Eine Aufgabe kann abgebrochen werden, sie wird jedoch nicht aus der Warteschlange entfernt, bis sie abgeschlossen ist. Die NSOperation-Klasse ist abstrakt und kann daher nicht direkt im Programm verwendet werden. Stattdessen gibt es zwei Unterklassen, die NSInvocationOperation Klasse und die NSBlockOperation Klasse. Ich werde die erste in diesem Tutorial verwenden.


Das Beispielprojekt

Hier ist das Ziel dieses Tutorials: Für jeden zusätzlichen Thread möchten wir, dass unsere Anwendung ein NSInvocationOperation (NSOperation) -Objekt erstellt. Wir fügen jedes Objekt der NSOperationQueue hinzu und sind dann fertig. Die Warteschlange übernimmt alles und die App funktioniert ohne Einfrieren. Um die Verwendung der oben genannten Klassen deutlich zu machen, erstellen wir ein (einfaches) Beispielprojekt, in dem neben dem Haupt-Thread der App zwei weitere Threads ausgeführt werden. Im ersten Thread wird eine Schleife von 1 bis 10.000.000 ausgeführt, und alle 100 Schritte wird ein Label mit dem Zählerwert der Schleife aktualisiert. Im zweiten Thread wird der Hintergrund eines Labels mit einer benutzerdefinierten Farbe gefüllt. Dieser Vorgang findet innerhalb einer Schleife statt und wird mehr als einmal ausgeführt. Wir haben also so etwas wie einen Farbrotator. Gleichzeitig werden die RGB-Werte der benutzerdefinierten Hintergrundfarbe und der Wert des Schleifenzählers neben der Beschriftung angezeigt. Schließlich verwenden wir drei Schaltflächen, um die Hintergrundfarbe der Ansicht im Haupt-Thread zu ändern. Diese Aufgaben konnten ohne Multitasking nicht gleichzeitig ausgeführt werden. Hier ist ein Blick auf das Endergebnis:


Schritt 1: Erstellen Sie das Projekt

Beginnen wir mit der Erstellung des Projekts. Öffnen Sie den Xcode und erstellen Sie einen neuen Einzelansicht-Anwendung.

Klicken Sie auf Weiter und geben Sie einen Namen für das Projekt ein. Ich habe es benannt ThreadingTestApp. Sie können denselben oder einen beliebigen anderen Namen verwenden.

Nächster. Beenden Sie die Projekterstellung.


Schritt 2: Richten Sie die Schnittstelle ein

Klicke auf das ViewController.xib Datei, um den Interface Builder anzuzeigen. Fügen Sie die folgenden Steuerelemente hinzu, um eine Schnittstelle wie das nächste Bild zu erstellen:

  1. UINavigationBar
    • Rahmen (x, y, W, H): 0, 0, 320, 44
    • Tintcolor: Schwarze Farbe
    • Titel: "Einfache Multi-Threading-Demo"
  2. UILabel
    • Rahmen (x, y, W, H): 20, 59, 280, 21
    • Text: "Zähler bei Thread 1"
  3. UILabel
    • Rahmen (x, y, W, H): 20, 88, 280, 50
    • Hintergrundfarbe: Hellgraue Farbe
    • Textfarbe: Dunkelgraue Farbe
    • Text: -
  4. UILabel
    • Rahmen (x, y, W, H): 20, 154, 280, 21
    • Text: "Random Color Rotator in Thread # 2"
  5. UILabel
    • Rahmen (x, y, W, H): 20, 183, 100, 80
    • Hintergrundfarbe: Hellgraue Farbe
    • Text: -
  6. UILabel
    • Rahmen (x, y, W, H): 128, 183, 150, 80
    • Text: -
  7. UILabel
    • Rahmen (x, y, W, H): 20, 374, 280, 21
    • Text: "Hintergrundfarbe im Hauptfaden"
  8. UIButton
    • Rahmen (x, y, W, H): 20, 403, 73, 37
    • Titel: "Farbe Nr. 1"
  9. UIButton
    • Rahmen (x, y, W, H): 124, 403, 73, 37
    • Titel: "Farbe # 2"
  10. UIButton
    • Rahmen (x, y, W, H): 228, 403, 73, 37
    • Titel: "Farbe # 3"

Stellen Sie für das letzte UILabel und die drei UIButtons das Autosizing Wert zu Links unten Damit das Interface auf dem iPhone 4 / 4S und iPhone 5 genauso gut aussieht wie das nächste Bild:


Schritt 3: IBOutlet-Eigenschaften und IBAction-Methoden

In diesem nächsten Schritt erstellen wir die IBOutlet-Eigenschaften und IBAction-Methoden, die erforderlich sind, damit unsere Beispiel-App funktioniert. Um neue Eigenschaften und Methoden zu erstellen und sie als Interface Builder mit Ihren Steuerelementen zu verbinden, klicken Sie in der Xcode-Symbolleiste auf die mittlere Schaltfläche der Schaltfläche Editor, um das Dialogfeld anzuzeigen Schnittassistenz:

Nicht jede Steuerung benötigt eine Outlet-Eigenschaft. Wir werden nur einen für die UILabels hinzufügen 3, 5 und 6 (entsprechend der Reihenfolge wurden sie in Schritt 2 aufgeführt), genannt label1, label2 und label3.

So fügen Sie eine neue Outlet-Eigenschaft ein, Strg + Klick (Rechtsklick) auf eine Beschriftung> Klicken Sie auf den Eintrag Neuer Referenzierungsausgang> Ziehen Sie den Assistenten in den Assistenten. Geben Sie anschließend einen Namen für die neue Eigenschaft an, genau wie in den folgenden Bildern:

Einfügen einer neuen IBOutlet-Eigenschaft


Festlegen des IBOutlet-Eigenschaftennamens

Wiederholen Sie den obigen Vorgang dreimal, um die drei UILabels mit den Eigenschaften zu verbinden. In deinem ViewController.h Datei, für die Sie diese Eigenschaften festgelegt haben:

 @ property (keep, nonatomic) IBOutlet UILabel * label1; @ property (keep, nonatomic) IBOutlet UILabel * label2; @ property (keep, nonatomic) IBOutlet UILabel * label3;

Fügen Sie nun die IBAction-Methoden für die drei UIButtons hinzu. Jede Schaltfläche ändert die Hintergrundfarbe der Ansicht. Einfügen einer neuen IBAction-Methode, Strg + Klick (Rechtsklick) auf ein UIButton> Klicken Sie auf das Touch Up Inside> Ziehen und Ablegen in den Assistenten-Editor. Danach geben Sie einen Namen für die neue Methode an. Schauen Sie sich die folgenden Bilder und den nächsten Ausschnitt für die Methodennamen an:


Einfügen einer neuen IBAction-Methode
Festlegen des IBAction-Methodennamens

Wiederholen Sie den obigen Vorgang drei Mal, um jedes UIButton mit einer Aktionsmethode zu verbinden. Das ViewController.h Die Datei sollte nun Folgendes enthalten:

 - (IBAction) applyBackgroundColor1; - (IBAction) applyBackgroundColor2; - (IBAction) applyBackgroundColor3;

Die IBOutlet-Eigenschaften und IBAction-Methoden sind jetzt fertig. Wir können jetzt mit der Codierung beginnen.


Schritt 4: Das NSOperationQueue-Objekt und die erforderlichen aufgabenbezogenen Methodendeklarationen

Eine der wichtigsten Aufgaben, die wir tun müssen, ist die Angabe einer NSOperationQueue object (unsere Operationswarteschlange), das zur Ausführung unserer Aufgaben in sekundären Threads verwendet wird. Öffne das ViewController.h Datei und fügen Sie den folgenden Inhalt direkt nach dem hinzu @Schnittstelle Header (Vergessen Sie nicht die geschweiften Klammern):

 @Interface ViewController: UIViewController NSOperationQueue * operationQueue; 

Außerdem muss für jede Task mindestens eine Methode vorhanden sein, die den Code enthält, der gleichzeitig mit dem Hauptthread ausgeführt wird. Gemäß der einleitenden Beschreibung wird die Methode als erste Aufgabe benannt counterTask und der zweite wird benannt colorRotatorTask:

 -(void) counterTask; - (void) colorRotatorTask;

Das ist alles was wir brauchen. Unsere ViewController.h Datei sollte so aussehen:

 @Interface ViewController: UIViewController NSOperationQueue * operationQueue;  @ property (keep, nonatomic) IBOutlet UILabel * label1; @ property (keep, nonatomic) IBOutlet UILabel * label2; @ property (keep, nonatomic) IBOutlet UILabel * label3; - (IBAction) applyBackgroundColor1; - (IBAction) applyBackgroundColor2; - (IBAction) applyBackgroundColor3; - (nichtig) counterTask; - (void) colorRotatorTask; @Ende

Lassen Sie uns zur Implementierung übergehen.


Schritt 5: Implementierung

Wir sind fast fertig. Wir haben unsere Schnittstelle eingerichtet, alle erforderlichen Verbindungen hergestellt, alle erforderlichen IBAction-Methoden und andere Methoden angegeben und unsere Basis festgelegt. Jetzt ist es Zeit, auf ihnen aufzubauen.

Öffne das ViewController.m Datei und gehen Sie zum viewDidLoad Methode. Der wichtigste Teil dieses Tutorials wird hier stattfinden. Wir werden ein neues schaffen NSOperationQueue Instanz und zwei NSOperation (NSInvocationOperation) Objekte. Diese Objekte kapseln den Code der beiden Methoden, die wir zuvor deklariert haben, und werden dann eigenständig von der ausgeführt NSOperationQueue. Hier ist der Code:

 - (void) viewDidLoad [super viewDidLoad]; // Erstellen Sie eine neue NSOperationQueue-Instanz. operationQueue = [NSOperationQueue neu]; // Erstellen Sie ein neues NSOperation-Objekt mit der Unterklasse NSInvocationOperation. // Sollen die counterTask-Methode ausgeführt werden? NSInvocationOperation * operation = [[NSInvocationOperation allocation] initWithTarget: Selbstselektor: @selector (counterTask) Objekt: nil]; // Fügen Sie die Operation der Warteschlange hinzu und lassen Sie sie ausgeführt werden. [operationQueue addOperation: operation]; [Betriebsfreigabe]; // Dieselbe Geschichte wie oben, sagen Sie hier, um die colorRotatorTask-Methode auszuführen. operation = [[NSInvocationOperation allocation] initWithTarget: Selbstselektor: @selector (colorRotatorTask) Objekt: keine] [operationQueue addOperation: operation]; [Betriebsfreigabe]; 

Dieser ganze Prozess ist sehr einfach. Nach dem Erstellen der NSOperationQueue Instanz erstellen wir ein NSInvocationOperation-Objekt (Operation). Wir legen die Selektormethode (den Code, den wir in einem separaten Thread ausführen möchten) fest und fügen ihn der Warteschlange hinzu. Sobald die Warteschlange erreicht ist, wird sie sofort ausgeführt. Danach kann das Operationsobjekt freigegeben werden, da die Warteschlange von nun an für die Behandlung verantwortlich ist. In diesem Fall erstellen wir ein anderes Objekt und verwenden es auf dieselbe Weise für die zweite Aufgabe (colorRotatorTask)..

Als nächstes müssen wir die beiden Selektormethoden implementieren. Beginnen wir mit dem Schreiben der counterTask Methode. Es wird ein enthalten zum Schleife, die für eine große Anzahl von Iterationen und alle 100 Schritte ausgeführt wird label1Text wird mit dem Zählerwert der aktuellen Iteration aktualisiert (ich). Der Code ist einfach, also hier ist alles:

 -(void) counterTask // Machen Sie eine GROSSE Schleife und lassen Sie alle 100 Schritte die UILabel label1 mit dem Wert des Zählers aktualisieren. für (int i = 0; i<10000000; i++)  if (i % 100 == 0)  // Notice that we use the performSelectorOnMainThread method here instead of setting the label's value directly. // We do that to let the main thread to take care of showing the text on the label // and to avoid display problems due to the loop speed. [label1 performSelectorOnMainThread:@selector(setText:) withObject:[NSString stringWithFormat:@"%d", i] waitUntilDone:YES];   // When the loop gets finished then just display a message. [label1 performSelectorOnMainThread:@selector(setText:) withObject:@"Thread #1 has finished." waitUntilDone:NO]; 

Beachten Sie, dass es (auch von Apple) empfohlen wird, visuelle Aktualisierungen der Benutzeroberfläche mit dem Haupt-Thread und nicht direkt von einem Sekundär-Thread aus durchzuführen. Daher die Verwendung der performSelectorOnMainThread In solchen Fällen ist eine Methode erforderlich.

Lassen Sie uns nun das implementieren colorRotatorTask Methode:

 -(void) colorRotatorTask // Wir benötigen eine benutzerdefinierte Farbe, um damit arbeiten zu können. UIColor * customColor; // Eine Schleife mit 500 Iterationen ausführen. für (int i = 0; i<500; i++)  // Create three float random numbers with values from 0.0 to 1.0. float redColorValue = (arc4random() % 100) * 1.0 / 100; float greenColorValue = (arc4random() % 100) * 1.0 / 100; float blueColorValue = (arc4random() % 100) * 1.0 / 100; // Create our custom color. Keep the alpha value to 1.0. customColor = [UIColor colorWithRed:redColorValue green:greenColorValue blue:blueColorValue alpha:1.0]; // Change the label2 UILabel's background color. [label2 performSelectorOnMainThread:@selector(setBackgroundColor:) withObject:customColor waitUntilDone:YES]; // Set the r, g, b and iteration number values on label3. [label3 performSelectorOnMainThread:@selector(setText:) withObject:[NSString stringWithFormat:@"Red: %.2f\nGreen: %.2f\nBlue: %.2f\Iteration #: %d", redColorValue, greenColorValue, blueColorValue, i] waitUntilDone:YES]; // Put the thread to sleep for a while to let us see the color rotation easily. [NSThread sleepForTimeInterval:0.4];  // Show a message when the loop is over. [label3 performSelectorOnMainThread:@selector(setText:) withObject:@"Thread #2 has finished." waitUntilDone:NO]; 

Sie können sehen, dass wir die verwendet haben performSelectorOnMainThread Methode auch hier. Der nächste Schritt ist der [NSThread sleepForTimeInterval: 0,4]; Befehl, der verwendet wird, um bei jeder Ausführung der Schleife eine kurze Verzögerung (0,4 Sekunden) zu verursachen. Auch wenn diese Methode nicht erforderlich ist, ist es vorzuziehen, sie hier zu verwenden, um die Änderungsgeschwindigkeit der Hintergrundfarbe zu verringern label2 UILabel (unser Farbrotator). Zusätzlich erstellen wir in jeder Schleife zufällige Werte für Rot, Grün und Blau. Wir legen diese Werte dann fest, um eine benutzerdefinierte Farbe zu erzeugen, und legen sie als Hintergrundfarbe im fest label2 UILabel.

Zu diesem Zeitpunkt sind die beiden Tasks fertig, die gleichzeitig mit dem Haupt-Thread ausgeführt werden. Lassen Sie uns die drei (wirklich einfachen) IBAction-Methoden implementieren und dann können wir loslegen. Wie bereits erwähnt, ändern die drei UIButtons die Hintergrundfarbe der Ansicht, mit dem ultimativen Ziel zu demonstrieren, wie der Haupt-Thread neben den anderen beiden Aufgaben ausgeführt werden kann. Hier sind sie:

 - (IBAction) applyBackgroundColor1 [self.view setBackgroundColor: [UIColor colorWithRed: 255.0 / 255.0 grün: 204.0 / 255.0 blau: 102.0 / 255.0 alpha: 1.0]];  - (IBAction) applyBackgroundColor2 [self.view setBackgroundColor: [UIColor colorWithRed: 204.0 / 255.0 grün: 255.0 / 255.0 blau: 102.0 / 255.0 alpha: 1.0]];  - (IBAction) applyBackgroundColor3 [self.view setBackgroundColor: [UIColor whiteColor]]; 

Das ist es! Jetzt können Sie die Anwendung ausführen und sehen, wie drei verschiedene Aufgaben gleichzeitig ausgeführt werden können. Denken Sie daran, dass die Ausführung von NSOperation-Objekten automatisch die Warteschlange verlässt.


Fazit

Viele von Ihnen haben möglicherweise bereits festgestellt, dass der eigentliche Code zum Ausführen einer Multi-Tasking-App nur wenige Zeilen Code erfordert. Es scheint, dass die größte Arbeitslast die Implementierung der erforderlichen Methoden ist, die mit jeder Aufgabe funktionieren. Dennoch ist diese Methode eine einfache Möglichkeit, Multi-Threading-Apps in iOS zu entwickeln.