Lucrul cu clasa NSOperationQueue

Multi-tasking împiedică înghețarea aplicațiilor. În majoritatea limbajelor de programare, realizarea acestui lucru este puțin complicată, dar clasa NSOperationQueue din iOS ușurează!

Acest tutorial va demonstra cum să utilizați NSOperationQueue clasă. Un obiect NSOperationQueue este o coadă care manipulează obiecte din NSOperation de tip clasă. Un obiect NSOperație, pur și simplu formulat, reprezintă o singură sarcină, care include atât datele cât și codul aferent sarcinii. NSOperationQueue gestionează și gestionează executarea tuturor obiectelor NSOperation (sarcinile) care au fost adăugate la acesta. Execuția are loc cu firul principal al aplicației. Când un obiect NSOperation este adăugat la coadă, acesta este executat imediat și nu părăsă coada până când nu este terminată. O sarcină poate fi anulată, dar nu este eliminată din coadă până când nu este terminată. Clasa NSOperation este una abstractă, deci nu poate fi utilizată direct în program. În schimb, există două subclase furnizate, NSInvocationOperation clasa și NSBlockOperation clasă. Voi folosi primul din acest tutorial.


Proiectul de probă

Iată scopul acestui tutorial: pentru fiecare fir suplimentar dorim ca aplicația noastră să creeze un obiect NSInvocationOperation (NSOperation). Vom adăuga fiecare obiect în NSOperationQueue și apoi vom termina. Coada se ocupă de tot și aplicația funcționează fără îngheț. Pentru a demonstra în mod clar utilizarea claselor pe care le-am menționat mai sus, vom crea un exemplu de proiect simplu în care, în afară de firul principal al aplicației, vom avea încă două fire care rulează împreună cu ea. La primul fir, o buclă va rula de la 1 la 10.000.000 și la fiecare 100 de pași o etichetă va fi actualizată cu valoarea contorului buclă. Pe cel de-al doilea fir, fundalul unei etichete se va umple cu o culoare personalizată. Acest proces va avea loc într-o buclă și va fi executat de mai multe ori. Așa că vom avea ceva de genul rotator de culoare. În același timp, valorile RGB ale culorii de fundal personalizate împreună cu valoarea contorului buclă vor fi afișate lângă etichetă. În cele din urmă, vom folosi trei butoane pentru a schimba culoarea de fundal a vederii pe firul principal. Aceste sarcini nu au putut fi executate simultan fără multi-tasking. Iată o privire la rezultatul final:


Pasul 1: Creați Proiectul

Să începem prin crearea proiectului. Deschideți Xcode-ul și creați un nou Vizualizare individuală.

Faceți clic pe Următorul și setați un nume pentru proiect. Am numit-o ThreadingTestApp. Puteți utiliza același nume sau orice alt nume doriți.

Următor →. finalizează crearea proiectului.


Pasul 2: Configurați interfața

Faceți clic pe ViewController.xib fișier pentru a dezvălui Interface Builder. Adăugați următoarele controale pentru a crea o interfață ca următoarea imagine:

  1. UINavigationBar
    • Cadru (x, y, W, H): 0, 0, 320, 44
    • Tintcolor: Culoare neagră
    • Titlu: "Simplă Multi-Threading Demo"
  2. UILabel
    • Cadru (x, y, W, H): 20, 59, 280, 21
    • Text: "Counter la Subiect # 1"
  3. UILabel
    • Cadru (x, y, W, H): 20, 88, 280, 50
    • Culoare fundal: Culoare gri deschis
    • Culoarea textului: Culoarea gri închis
    • Text: -
  4. UILabel
    • Cadru (x, y, W, H): 20, 154, 280, 21
    • Text: "Rotator de culoare aleator la Subiect # 2"
  5. UILabel
    • Cadru (x, y, W, H): 20, 183, 100, 80
    • Culoare fundal: Culoare gri deschis
    • Text: -
  6. UILabel
    • Cadru (x, y, W, H): 128, 183, 150, 80
    • Text: -
  7. UILabel
    • Cadru (x, y, W, H): 20, 374, 280, 21
    • Text: "Culoarea fundalului la subiectul principal"
  8. UIButton
    • Cadru (x, y, W, H): 20, 403, 73, 37
    • Titlu: "Culoarea # 1"
  9. UIButton
    • Cadru (x, y, W, H): 124, 403, 73, 37
    • Titlu: "Culoarea # 2"
  10. UIButton
    • Cadru (x, y, W, H): 228, 403, 73, 37
    • Titlu: "Culoarea # 3"

Pentru ultimul UILabel și cele trei UIButtons, setați Autosizing valoare pentru Stânga - de jos pentru a face interfața să arate frumos pe iPhone 4 / 4S și iPhone 5, la fel ca următoarea imagine:


Pasul 3: Proprietățile IBOutlet și metodele IBAction

În acest pas următor vom crea proprietățile IBOutlet și metodele IBAction necesare pentru a face ca exemplele de aplicații să funcționeze. Pentru a crea noi proprietăți și metode și a le conecta la comenzile dvs. în timp ce sunteți Interface Builder, faceți clic pe butonul din mijloc al butonului Editor din bara de instrumente Xcode pentru a dezvălui Editor asistent:

Nu orice comandă are nevoie de o proprietate de ieșire. Vom adăuga doar una pentru UILabels 3, 5 și 6 (în ordinea în care au fost enumerate în etapa 2), numit eticheta1, eticheta2 și eticheta3.

Pentru a insera o proprietate nouă a prizei, Control + Faceți clic pe (Faceți clic dreapta) pe o etichetă> Faceți clic pe ieșirea de referință nouă> Trageți și plasați în editorul asistent. După aceea, specificați un nume pentru noua proprietate, la fel ca în următoarele imagini:

Introducerea unei noi proprietăți IBOutlet


Setarea numelui proprietății IBOutlet

Repetați procesul de mai sus de trei ori pentru a conecta cele trei UILabeli la proprietăți. În interiorul tău ViewController.h fișierul pe care le-ați declarat aceste proprietăți:

 @property (reține, nonatomic) IBOutlet UILabel * label1; @property (reține, nonatomic) IBOutlet UILabel * label2; @property (reține, nonatomic) IBOutlet UILabel * label3;

Acum adăugați metodele IBAction pentru cele trei butoane UIB. Fiecare buton va schimba culoarea de fundal a vederii. Pentru a introduce o nouă metodă IBAction, Control + Faceți clic pe (clic dreapta) pe un UIButton> Faceți clic pe Touch Up Inside> Trageți și plasați în editorul asistent. După aceea, specificați un nume pentru noua metodă. Consultați următoarele imagini și fragmentul următor pentru numele metodelor:


Introducerea unei noi metode IBAction
Setarea numelui metodei IBAction

Din nou, repetați procesul de mai sus de trei ori pentru a conecta fiecare UIButton la o metodă de acțiune. ViewController.h fișierul ar trebui să conțină acum următoarele:

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

Proprietățile IBOutlet și metodele IBAction sunt acum pregătite. Acum putem începe codarea.


Pasul 4: Obiectul NSOperationQueue și declarațiile de metodă legate de sarcini necesare

Una dintre cele mai importante sarcini pe care trebuie să le facem este să declarăm a NSOperationQueue obiect (coada noastră de operare), care va fi folosit pentru a executa sarcinile noastre în fire secundare. Deschide ViewController.h fișier și adăugați următorul conținut imediat după @interface antet (nu uitați parantezele curbate):

 @interface ViewController: UIViewController NSOperationQueue * operationQueue; 

De asemenea, fiecare sarcină trebuie să aibă cel puțin o metodă care conține codul care se va executa simultan cu firul principal. Conform descrierii introductive, prima sarcină va fi denumită metoda counterTask iar al doilea va fi numit colorRotatorTask:

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

Asta e tot ce avem nevoie. Al nostru ViewController.h fișierul ar trebui să arate astfel:

 @interface ViewController: UIViewController NSOperationQueue * operationQueue;  @property (reține, nonatomic) IBOutlet UILabel * label1; @property (reține, nonatomic) IBOutlet UILabel * label2; @property (reține, nonatomic) IBOutlet UILabel * label3; - (IBAction) applyBackgroundColor1; - (IBAction) applyBackgroundColor2; - (IBAction) applyBackgroundColor3; - counterTask (void); - (void) colorRotatorTask; @Sfârșit

Să trecem la punerea în aplicare.


Pasul 5: Implementarea

Aproape am terminat. Am stabilit interfața noastră, am făcut toate conexiunile necesare, am declarat orice IBAction și alte metode necesare și am stabilit baza noastră. Acum este timpul să le construim.

Deschide ViewController.m fișier și mergeți la viewDidLoad metodă. Cea mai importantă parte a acestui tutorial va avea loc aici. Vom crea un nou NSOperationQueue instanță și două Operațiunea NSO (NSInvocationOperation) obiecte. Aceste obiecte vor încapsula codul celor două metode pe care le-am declarat anterior și apoi vor fi executate pe cont propriu de către NSOperationQueue. Iată codul:

 - (vid) viewDidLoad [super viewDidLoad]; // Creați o nouă instanță NSOperationQueue. operationQueue = [NSOperationQueue new]; // Creați un nou obiect NSOperation utilizând subclasa NSInvocationOperation. // Spune-i să execute metoda counterTask. NSInvocationOperation * operație = [[NSInvocationOperation alloc] initWithTarget: selector auto: @selector (counterTask) object: nil]; // Adăugați operația în coadă și lăsați-o să fie executată. [operationQueue addOperation: operation]; [eliberarea operațiunii]; // Aceeași poveste ca mai sus, spuneți aici doar pentru a executa metoda colorRotatorTask. operație = [[NSInvocationOperation alloc] initWithTarget: selector auto: @selector (colorRotatorTask) object: nil]; [operationQueue addOperation: operation]; [eliberarea operațiunii]; 

Acest întreg proces este foarte simplu. După crearea fișierului NSOperationQueue de exemplu, vom crea un obiect NSInvocationOperation (operație). Am setat metoda selectorului (codul pe care dorim să îl executăm pe un fir separat) și apoi îl adăugăm la coadă. Odată ce intră în coadă, începe imediat să ruleze. După aceasta, obiectul de operare poate fi eliberat, deoarece coada de așteptare este responsabilă pentru manipularea ei de acum încolo. În acest caz, vom crea un alt obiect și îl vom folosi în același mod pentru cea de-a doua sarcină (colorRotatorTask).

Următoarea noastră sarcină este să implementăm cele două metode selector. Să începem prin a scrie counterTask metodă. Acesta va conține a pentru buclă care va rula pentru un număr mare de iterații și la fiecare 100 de pași label1textul va fi actualizat cu valoarea contorului curent al iterației (eu). Codul este simplu, deci aici este totul:

 -(void) counterTask // Faceți o buclă BIG și la fiecare 100 de pași lăsați-o să actualizeze label1 UILabel cu valoarea contorului. pentru (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]; 

Vă rugăm să rețineți că se recomandă ca cea mai bună practică (chiar de Apple) să efectueze orice actualizare vizuală a interfeței utilizând firul principal și nu făcând-o direct dintr-un fir secundar. Prin urmare, utilizarea performSelectorOnMainThread este necesară o metodă în astfel de cazuri.

Acum, să implementăm colorRotatorTask metodă:

 -(void) colorRotatorTask // Avem nevoie de o culoare particularizată pentru a lucra cu. UICoror * customColor; // Rulați o buclă cu 500 iterații. pentru (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]; 

Puteți vedea că am folosit-o performSelectorOnMainThread și aici. Următorul pas este [NSThread sleepForTimeInterval: 0.4]; comanda, care este folosit pentru a provoca o scurtă întârziere (0,4 secunde) în fiecare execuție buclă. Chiar dacă nu este necesar să se utilizeze această metodă, este preferabil să se folosească aici pentru a încetini viteza de schimbare a culorii de fundal a Label2 UILabel (rotatorul nostru de culori). În plus, în fiecare buclă se creează valori aleatorii pentru roșu, verde și albastru. Apoi, setăm aceste valori pentru a produce o culoare personalizată și pentru ao seta ca o culoare de fundal în Label2 UILabel.

În acest moment, cele două sarcini care vor fi executate în același timp cu firul principal sunt gata. Să implementăm cele trei metode IBAction (foarte ușor) și apoi suntem gata să mergem. Așa cum am menționat deja, cele trei UIButtons vor schimba culoarea de fundal a ecranului, scopul final fiind acela de a demonstra modul în care firul principal poate rula alături de celelalte două sarcini. Aici sunt ei:

 - (IBAction) applyBackgroundColor1 [auto.view setBackgroundColor: [UICcolor colorWithRed: 255.0 / 255.0 verde: 204.0 / 255.0 albastru: 102.0 / 255.0 alpha: 1.0]];  - (IBAction) applyBackgroundColor2 [auto.view setBackgroundColor: [UICcolor colorWithRed: 204.0 / 255.0 verde: 255.0 / 255.0 albastru: 102.0 / 255.0 alpha: 1.0]];  - (IBAction) applyBackgroundColor3 [auto.view setBackgroundColor: [UICcolor whiteColor]]; 

Asta e! Acum puteți rula aplicația și puteți vedea cum se pot desfășura simultan trei sarcini diferite. Rețineți că atunci când executarea obiectelor NSOperation sa terminat, va părăsi automat coada de așteptare.


Concluzie

Mulți dintre dvs. ați descoperit deja că codul real pentru a rula o aplicație multi-tasking necesită doar câteva rânduri de cod. Se pare că cel mai mare volum de lucru este implementarea metodelor necesare care funcționează cu fiecare sarcină. Cu toate acestea, această metodă este o modalitate ușoară de a dezvolta aplicații multi-threading în iOS.

Cod