Multi-tasking förhindrar att appar fryser. I de flesta programmeringsspråk är det lite svårt att uppnå detta, men NSOperationQueue-klassen i iOS gör det enkelt!
Denna handledning visar hur du använder NSOperationQueue klass. Ett NSOperationQueue-objekt är en kö som hanterar objekt av NSOperation klass typ. Ett NSOperation-objekt, enkelt formulerat, representerar en enda uppgift, inklusive både data och koden relaterad till uppgiften. NSOperationQueue hanterar och hanterar utförandet av alla NSOperation-objekten (de uppgifter) som har lagts till. Utförandet sker med huvudtråden i ansökan. När ett NSOperation-objekt läggs till i köen utförs det omedelbart och det lämnar inte köen tills det är klart. En uppgift kan avbrytas, men den tas inte bort från kön tills den är klar. NSOperationsklassen är en abstrakt, så den kan inte användas direkt i programmet. Istället finns det två underklasser, NSInvocationOperation klass och NSBlockOperation klass. Jag använder den första i den här handledningen.
Här är målet för denna handledning: För varje extra tråd vill vi att vår applikation ska skapa ett NSInvocationOperation (NSOperation) -objekt. Vi lägger till varje objekt i NSOperationQueue och då är vi färdiga. Kön tar ansvar för allt och appen fungerar utan frysning. För att tydligt visa användningen av de klasser som jag nämnde ovan kommer vi att skapa ett (enkelt) provprojekt där vi, förutom huvuddragen i appen, kommer att ha ytterligare två trådar som går tillsammans med den. På den första tråden kommer en slinga att gå från 1 till 10.000.000 och varje 100 steg kommer en etikett att uppdateras med slingens motvärde. På den andra tråden fyller en etiketts bakgrund med en anpassad färg. Den processen kommer att äga rum inuti en slinga och den kommer att utföras mer än en gång. Så vi kommer att ha något som en färgrotator. Samtidigt visas RGB-värdena för den anpassade bakgrundsfärgen tillsammans med slingräknarens värde bredvid etiketten. Slutligen använder vi tre knappar för att ändra vyens bakgrundsfärg på huvudtråden. Dessa uppgifter kunde inte utföras samtidigt utan multi-tasking. Här är en titt på slutresultatet:
Låt oss börja med att skapa projektet. Öppna Xcode och skapa en ny Enkel visningsprogram.
Klicka på Nästa och ange ett namn för projektet. Jag heter det ThreadingTestApp. Du kan använda samma eller något annat namn du vill.
Nästa. slutföra projektskapandet.
Klicka på ViewController.xib
fil för att avslöja gränssnittsbyggaren. Lägg till följande kontroller för att skapa ett gränssnitt som nästa bild:
För den sista UILabel och de tre UIButtonsna, ställ in Autosizing värde till Vänster - Bottom för att göra gränssnittet snyggt på iPhone 4 / 4S och iPhone 5, precis som nästa bild:
I det här nästa steget kommer vi att skapa IBOutlet-egenskaperna och IBAction-metoderna som är nödvändiga för att göra vårt exempelapparbete. Om du vill skapa nya egenskaper och metoder och ansluta dem till dina kontroller medan du är gränssnittsbyggare, klicka på mellansknappen på redigerarknappen på Xcode-verktygsfältet för att avslöja Assistent Editor:
Inte varje kontroll behöver en utloppsfastighet. Vi lägger bara till en för UILabels 3, 5 och 6 (enligt ordern var de listade i steg 2), som heter label1, label2 och label3.
För att infoga en ny utloppsegenskap, Control + Click (Högerklicka) på en etikett> Klicka på New Referencing Outlet> Dra och släpp in i Assistent Editor. Därefter ange ett namn på den nya egenskapen, precis som i följande bilder:
Infoga en ny IBOutlet-egenskap
Ställa in egenskapsbeteckningen för IBOutlet
Upprepa processen över tre gånger för att ansluta de tre UILabelsna till egenskaper. Inuti din ViewController.h
fil du har deklarerat dessa egenskaper:
@property (behåll, nonatomic) IBOutlet UILabel * label1; @property (behåll, nonatomic) IBOutlet UILabel * label2; @property (behåll, nonatomic) IBOutlet UILabel * label3;
Lägg nu till IBAction metoderna för de tre UIButtons. Varje knapp ändrar bakgrundsfärgen på vyn. För att infoga en ny IBAction-metod, Control + Click (Högerklicka) på en UIButton> Klicka på Touch Up Inside> Dra och släpp in i Assistent Editor. Därefter ange ett namn för den nya metoden. Ta en titt på följande bilder och nästa kod för metodnamnen:
Återre, upprepa processen över tre gånger för att ansluta varje UIButton till en åtgärdsmetod. De ViewController.h
filen ska nu innehålla följande:
- (IBAction) applyBackgroundColor1; - (IBAction) applyBackgroundColor2; - (IBAction) applyBackgroundColor3;
IBOutlet-egenskaperna och IBAction-metoderna är nu färdiga. Vi kan nu börja kodning.
En av de viktigaste uppgifterna vi måste göra är att deklarera en NSOperationQueue
objekt (vår driftskö), som kommer att användas för att utföra våra uppgifter i sekundära trådar. Öppna ViewController.h
fil och lägg till följande innehåll strax efter @gränssnitt
rubrik (glöm inte de krökta hakarna):
@interface ViewController: UIViewController NSOperationQueue * operationQueue;
Dessutom måste varje uppgift ha minst en metod som innehåller koden som kommer att köras samtidigt med huvudtråden. Enligt den inledande beskrivningen kommer den första uppgiften att metoden att namnges counterTask
och den andra kommer att namnges colorRotatorTask
:
-(Void) counterTask; - (void) colorRotatorTask;
Det är allt vi behöver. Vår ViewController.h
filen ska se så här ut:
@interface ViewController: UIViewController NSOperationQueue * operationQueue; @property (behåll, nonatomic) IBOutlet UILabel * label1; @property (behåll, nonatomic) IBOutlet UILabel * label2; @property (behåll, nonatomic) IBOutlet UILabel * label3; - (IBAction) applyBackgroundColor1; - (IBAction) applyBackgroundColor2; - (IBAction) applyBackgroundColor3; - (void) counterTask; - (void) colorRotatorTask; @slutet
Låt oss gå vidare till genomförandet.
Vi är nästan färdiga. Vi har inställt vårt gränssnitt, gjort alla nödvändiga anslutningar, förklarat nödvändig IBAction och andra metoder och etablerat vår bas. Nu är det dags att bygga på dem.
Öppna ViewController.m
fil och gå till viewDidLoad
metod. Den viktigaste delen av denna handledning kommer att äga rum här. Vi kommer att skapa en ny NSOperationQueue
exempel och två NSOperation (NSInvocationOperation)
objekt. Dessa objekt kommer att inkapsla koden för de två metoder som vi tidigare deklarerat och då kommer de att utföras på egen hand av NSOperationQueue
. Här är koden:
- (void) viewDidLoad [super viewDidLoad]; // Skapa en ny NSOperationQueue-förekomst. operationQueue = [NSOperationQueue new]; // Skapa ett nytt NSOperation-objekt med NSInvocationOperation-underklassen. // Berätta det för att köra counterTask-metoden. NSInvocationOperation * operation = [[NSInvocationOperation alloc] initWithTarget: självväljare: @selector (counterTask) objekt: nil]; // Lägg till operationen i kön och låt den utföras. [operationQUEue addOperation: operation]; [operation release]; // Samma historia som ovan, berätta här för att exekvera colorRotatorTask-metoden. operation = [[NSInvocationOperation alloc] initWithTarget: självväljare: @selector (colorRotatorTask) objekt: noll]; [operationQUEue addOperation: operation]; [operation release];
Hela processen är väldigt enkel. Efter att ha skapat NSOperationQueue
Exempelvis skapar vi ett NSInvocationOperation-objekt (operation). Vi ställer in sin väljare metod (koden vi vill ha kört på en separat tråd), och sedan lägger vi till den i kön. När det kommer in i kön börjar det omedelbart att springa. Därefter kan operationsobjektet släppas, eftersom kön ansvarar för att hantera det från och med nu. I det här fallet skapar vi ett annat objekt och vi använder det på samma sätt för den andra uppgiften (colorRotatorTask).
Vår nästa uppgift är att genomföra de två väljarmetoderna. Låt oss börja med att skriva counterTask
metod. Det kommer att innehålla en för
loop som kommer att springa för ett stort antal iterationer och varje 100 steg LABEL1
s text kommer att uppdateras med nuvarande iterationens motvärde (jag
). Koden är enkel, så här är allt:
-(void) counterTask // Gör en stor loop och varje 100 steg gör det uppdatera etiketten1 UILabel med räknarens värde. 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];
Observera att det rekommenderas som bästa praxis (även av Apple) att utföra visuella uppdateringar på gränssnittet med huvudgänget och inte genom att göra det direkt från en sekundär tråd. Därför användningen av performSelectorOnMainThread
Metod är nödvändig i fall som denna.
Nu ska vi genomföra colorRotatorTask
metod:
-(void) colorRotatorTask // Vi behöver en anpassad färg att arbeta med. UIColor * customColor; // Kör en slinga med 500 iterationer. 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];
Du kan se att vi använde performSelectorOnMainThread
metod här också. Nästa steg är [NSThread sleepForTimeInterval: 0.4];
kommando, som används för att orsaka en kort fördröjning (0,4 sekunder) i varje loop-körning. Även om det inte är nödvändigt att använda den här metoden är det bättre att använda det här för att sakta ner bakgrundsfärgens växlingshastighet LABEL2
UILabel (vår färgrotator). Dessutom skapar vi slumpmässiga värden för de röda, gröna och blåa i varje slinga. Vi ställer sedan in dessa värden för att skapa en anpassad färg och ange den som bakgrundsfärg i LABEL2
UILabel.
Vid den här tiden är de två uppgifterna som ska utföras samtidigt med huvudgängan redo. Låt oss genomföra de tre (riktigt lätta) IBAction-metoderna och då är vi redo att gå. Som jag redan har nämnt kommer de tre UIButtonsna att ändra visnings bakgrundsfärg, med det yttersta målet att visa hur huvudtråden kan springa vid sidan av de andra två uppgifterna. Här är de:
- (IBAction) applyBackgroundColor1 [self.view setBackgroundColor: [UIColor colorWithRed: 255.0 / 255.0 green: 204.0 / 255.0 blue: 102.0 / 255.0 alpha: 1.0]]; - (IBAction) applyBackgroundColor2 [self.view setBackgroundColor: [UIColor colorWithRed: 204.0 / 255.0 green: 255.0 / 255.0 blue: 102.0 / 255.0 alpha: 1.0]]; - (IBAction) applyBackgroundColor3 [self.view setBackgroundColor: [UIColor whiteColor]];
Det är allt! Nu kan du köra programmet och se hur tre olika uppgifter kan äga rum samtidigt. Kom ihåg att när körningen av NSOperation-objekt är över, lämnar den automatiskt köen.
Många av er kanske redan har upptäckt att den faktiska koden för att köra en multi-tasking-app bara kräver några rader av kod. Det verkar som att den största arbetsbelastningen genomför de nödvändiga metoderna som arbetar med varje uppgift. Ändå är den här metoden ett enkelt sätt att utveckla multi-threading-appar i IOS.