În tutorialul anterior, te-am prezentat NSURLSession
. Am vorbit despre avantajele pe care le-a avut NSURLConnection
și cum se utilizează NSURLSession
pentru sarcini simple, cum ar fi preluarea datelor de la un serviciu web și descărcarea unei imagini de pe web. În acest tutorial, vom examina mai îndeaproape opțiunile de configurare din NSURLSession
și cum să anulați și să reluați o sarcină de descărcare. Avem o mulțime de teren pentru a acoperi așa că să începem.
După cum am văzut în tutorialul anterior, o sesiune, o instanță de NSURLSession
, este un container configurabil pentru introducerea cererilor de rețea. Configurația sesiunii este tratată de o instanță de NSURLSessionConfiguration
.
Un obiect de configurare a sesiunii nu este altceva decât un dicționar al proprietăților care definește cum se comportă sesiunea pe care este legată. O sesiune are un obiect de configurare a sesiunii care dictează politicile cookie-ului, securității și cache-ului, numărul maxim de conexiuni la o gazdă, timeout-urile resurselor și rețelei etc. Aceasta reprezintă o îmbunătățire semnificativă față de NSURLConnection
, care sa bazat pe un obiect de configurație global cu mult mai puțină flexibilitate.
Odată ce o sesiune este creată și configurată de a NSURLSessionConfiguration
exemplu, configurația sesiunii nu poate fi modificată. Dacă trebuie să modificați configurația unei sesiuni, trebuie să creați o nouă sesiune. Rețineți că este posibil să copiați configurația unei sesiuni și să o modificați, dar modificările nu au niciun efect asupra sesiunii din care a fost copiată configurația.
NSURLSessionConfiguration
clasa oferă trei constructori din fabrică pentru configurarea configurațiilor standard, defaultSessionConfiguration
, ephemeralSessionConfiguration
, și backgroundSessionConfiguration
. Prima metodă returnează o copie a textului setarea implicită a sesiunii obiect, care are ca rezultat o sesiune care se comportă similar cu o sesiune NSURLConnection
obiect în configurația sa standard. Modificarea unei configurații de sesiune obținută prin defaultSessionConfiguration
metoda de fabrică nu modifică configurația sesiunii implicite pe care o copiază.
Un obiect de configurare a sesiunii creat prin invocarea ephemeralSessionConfiguration
metoda de fabrică asigură că sesiunea rezultată nu utilizează stocări permanente pentru cookie-uri, cache-uri sau acreditări. Cu alte cuvinte, cookie-urile, cache-urile și acreditările sunt păstrate în memorie. Sesiunile efemere sunt, prin urmare, ideale dacă aveți nevoie pentru a implementa navigarea privată, ceva care pur și simplu nu a fost posibil înainte de introducerea NSURLSession
.
backgroundSessionConfiguration:
metoda fabrică creează un obiect de configurare a sesiunii care permite încărcările și descărcările în afara procesului. Sarcinile de încărcare și descărcare sunt gestionate de un daemon de fundal și continuă să ruleze chiar dacă aplicația este suspendată sau blocată. Vom vorbi mai târziu despre sesiunile de fundal din această serie.
După cum am văzut în tutorialul anterior, crearea unui obiect de configurare a sesiunii este simplă. În exemplul prezentat mai jos, am folosit funcția defaultSessionConfiguration
metoda fabricii pentru a crea o NSURLSessionConfiguration
instanță. Configurarea unui obiect de configurare a sesiunii este la fel de simplă ca modificarea proprietăților sale, așa cum se arată în exemplu. Apoi putem folosi obiectul de configurare a sesiunii pentru a instantiza un obiect de sesiune. Obiectul de sesiune funcționează ca o fabrică pentru sarcini de date, încărcare și descărcare, fiecare activitate corespund unei singure solicitări. În exemplul de mai jos, interogăm API-ul de căutare iTunes ca în tutorialul precedent.
// Crearea configurării sesiunii NSURLSessionConfiguration * sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration]; // Configurați configurarea sesiunii [sessionConfiguration setAllowsCellularAccess: YES]; [sessionConfiguration setHTTPAdditionalHeaders: @ @ "Accept": @ "application / json"]; // Creați sesiune NSURLSession * session = [NSURLSession sessionWithConfiguration: sessionConfiguration]; // Trimite cererea NSURL * url = [NSURL URLWithString: @ "https://itunes.apple.com/search?term=apple&media=software"]; [[data sesiuneTaskWithURL: url completionHandler: ^ (NSData * date, răspuns NSURLResponse *, eroare NSError *) NSLog (@ "% @", [NSJSONSerializare JSONObjectWithData: opțiuni de date: 0 eroare: zero]); ] relua];
Exemplul ilustrează, de asemenea, cât de ușor este să adăugați anteturi personalizate prin setarea HTTPAdditionalHeaders
proprietatea obiectului de configurare a sesiunii. Frumusetea NSURLSession
API este că fiecare solicitare care trece prin sesiune este configurată de obiectul de configurare a sesiunii. Adăugarea antetelor de autentificare la un set de solicitări, de exemplu, devine ușor ca plăcintă.
În tutorialul anterior, v-am arătat cum să descărcați o imagine utilizând NSURLSession
API-ul. Cu toate acestea, conexiunile de rețea nu sunt fiabile și se întâmplă prea des ca descărcarea să se deterioreze datorită unei conexiuni slabe la rețea. Din fericire, reluarea descărcării nu este dificilă cu NSURLSession
API-ul. În următorul exemplu, vă voi arăta cum să anulați și să reluați descărcarea unei imagini.
Înainte de a examina mai atent reluarea unei sarcini de descărcare, este important să înțelegeți diferența dintre anularea și suspendarea unei sarcini de descărcare. Este posibil să suspendați o sarcină de descărcare și să o reluați mai târziu. Anularea unei sarcini de descărcare, însă, oprește sarcina și nu este posibilă reluarea acesteia mai târziu. Există însă o alternativă. Este posibil să anulați o sarcină de descărcare apelând cancelByProducingResumeData:
pe el. Acceptă un handler de completare care acceptă un parametru, un NSData
obiect care este folosit pentru a relua descărcarea ulterioară prin invocarea downloadTaskWithResumeData:
sau downloadTaskWithResumeData: completionHandler:
pe un obiect de sesiune. NSData
obiect conține informațiile necesare pentru a relua sarcina de descărcare unde a rămas.
Deschideți proiectul pe care l-am creat în tutorialul precedent sau îl descărcați aici. Începem prin adăugarea a două butoane la interfața cu utilizatorul, una pentru a anula descărcarea și una pentru a relua descărcarea. În fișierul header al controlerului de vizualizare, creați o priză și o acțiune pentru fiecare buton, după cum se arată mai jos.
#import@interface MTViewController: UIViewController @property (slab, nonatomic) IBOutlet UIButton * cancelButton; @property (slab, nonatomic) IBOutlet UIButton * resumeButton; @property (slab, nonatomic) IBOutlet UIImageView * imageView; @property (slab, nonatomic) IBOutlet UIProgressView * progressView; - (IBAction) anulați: (id) expeditor; - (IBAction) CV: (id) expeditor; @Sfârșit
Deschideți tabloul de bord principal al proiectului și adăugați două butoane la vizualizarea controlerului de vizualizare. Poziționați butoanele așa cum se arată în imaginea de mai jos și conectați fiecare buton cu priza și acțiunea corespunzătoare.
Va trebui să facem niște refactorizări pentru ca totul să funcționeze corect. Deschis MTViewController.m
și să declare o variabilă de instanță și două proprietăți. Variabila instanței, sesiune
, va păstra o referință la sesiunea pe care o vom folosi pentru descărcarea imaginii.
#import "MTViewController.h" @interface MTViewController ()NSURLSession * _session; @property (puternic, nonatomic) NSURLSessionDownloadTask * downloadTask; @property (puternic, nonatomic) NSData * resumeData; @Sfârșit
Trebuie, de asemenea, să refactorizăm viewDidLoad
, dar mai întâi aș vrea să pun în aplicare o metodă getter pentru sesiune. Implementarea sa este destul de simplă, după cum puteți vedea mai jos. Creăm un obiect de configurare a sesiunii folosind defaultSessionConfiguration
fabrică și instanțiați obiectul sesiunii cu acesta. Controlerul de vizualizare servește drept delegat al sesiunii.
- (NSURLSession *) sesiune if (! _Session) // Configurarea sesiunii de configurare NSURLSessionConfiguration * sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration]; // Creați sesiunea _session = [NSURLSession sessionWithConfiguration: delegația sesiuniiConfiguration: self delegateQueue: nil]; retur _session;
Cu sesiune
accesoriu implementat, viewDidLoad
metoda devine mult mai simplă. Creați o sarcină de descărcare, așa cum am făcut în tutorialul anterior, și stocați o referință la sarcina din downloadTask
. Apoi spunem sarcina de descărcare relua
.
- (vid) viewDidLoad [super viewDidLoad]; // Creați sarcina de descărcare self.downloadTask = [auto.session downloadTaskWithURL: [NSURL URLWithString: @ "http://cdn.tutsplus.com/mobile/uploads/2014/01/5a3f1-sample.jpg"]]; // Reluați sarcina de descărcare [self.downloadTask resume];
Anulare:
acțiune conține logica pentru anularea sarcinii de descărcare pe care tocmai am creat-o. Dacă downloadTask
nu este zero
, noi sunam cancelByProducingResumeData:
cu privire la sarcină. Această metodă acceptă un parametru, un bloc de completare. Blocul de completare are de asemenea un parametru, un exemplu de NSData
. Dacă resumeData
nu este zero
, stocăm o referință la obiectul de date în vizualizarea controlerului resumeData
proprietate.
Dacă descărcarea nu poate fi reluată, blocul de completare resumeData
este parametrul zero
. Nu toate descărcările sunt reluabile, deci este important să verificați dacă resumeData
este valabil NSData
obiect.
- (IBAction) anulați: (id) expeditor if (! Self.downloadTask) retur; // Ascundeți butonul Anulare [auto.cancelButton setHidden: YES]; [auto.downloadTask cancelByProducingResumeData: ^ (NSData * resumeData) dacă (! resumeData) retur; [auto setResumeData: resumeData]; [auto setDownloadTask: zero]; ];
Reluarea sarcinii de descărcare după ce a fost anulată este ușoară. În relua:
acțiune, verificăm dacă controlerul de vizualizare resumeData
proprietatea este setată. Dacă resumeData
este valabil NSData
obiect, spunem sesiune
obiect de a crea o nouă sarcină de descărcare și de ao transmite NSData
obiect. Toate acestea sunt sesiune
obiect trebuie să recreeze sarcina de descărcare pe care am anulat-o în Anulare:
acțiune. Apoi spunem sarcina de descărcare relua
și stabilit resumeData
la zero
.
- (IBAction) relua: (id) expeditorul if (! Self.resumeData) retur; // Ascundeți butonul Resume [auto.resumeButton setHidden: YES]; // Creați sarcina de descărcare self.downloadTask = [auto.session downloadTaskWithResumeData: self.resumeData]; // Reluați sarcina de descărcare [self.downloadTask resume]; // Curățire [auto setResumeData: zero];
Construiți proiectul și rulați aplicația în Simulatorul iOS sau pe un dispozitiv fizic. Descărcarea ar trebui să înceapă automat. Atingeți butonul anulare pentru a anula descărcarea și atingeți butonul CV pentru a relua descărcarea.
Există o serie de detalii pe care trebuie să le avem în vedere. În primul rând, butoanele nu ar trebui să fie întotdeauna vizibile. Vom folosi observarea valorilor cheie pentru a afișa și a ascunde butoanele atunci când este necesar. În viewDidLoad
, ascundeți butoanele și adăugați controlerul de vizualizare ca observator al acesteia pentru resumeData
și downloadTask
căi cheie.
- (vid) viewDidLoad [super viewDidLoad]; // Add Observer [self addObserver: self forKeyPath: @ "resumeData" opțiuni: NSKeyValueObservingOptionNew context: NULL]; [self addObserver: auto pentruKeyPath: @ "opțiunea" downloadTask ": NSKeyValueObservingOptionNew context: NULL]; // Configurare interfață utilizator [self.cancelButton setHidden: YES]; [self.resumeButton setHidden: YES]; // Creați sarcina de descărcare self.downloadTask = [auto.session downloadTaskWithURL: [NSURL URLWithString: @ "http://cdn.tutsplus.com/mobile/uploads/2014/01/5a3f1-sample.jpg"]]; // Reluați sarcina de descărcare [self.downloadTask resume];
În observeValueForKeyPath: ofObject: schimbare: Context:
, ascundem butonul de anulare dacă resumeData
este zero
și ascundem butonul de reluare dacă downloadTask
este zero
. Construiți proiectul și rulați aplicația încă o dată pentru a vedea rezultatul. Asa este mai bine. Dreapta?
- (void) observeValueForKeyPath: (NSString *) cheiePath ofObject: (id) schimbare obiect: (NSDictionary *) context de schimbare: (void *) context if ([keyPath esteEqualToString: @ "resumeData"]) dispatch_async (dispatch_get_main_queue ^ [auto.resumeButton setHidden: (auto.resumeData == nil)];); altfel dacă ([keyPath esteEqualToString: @ "downloadTask"]) dispatch_async (dispatch_get_main_queue (), ^ [auto.cancelButton setHidden: (auto.downloadTask == nil)];);După cum subliniază George Yang în comentarii, nu știm dacă
observeValueForKeyPath: ofObject: schimbare: Context:
este chemat pe firul principal. Prin urmare, este important să actualizați interfața de utilizator într-un bloc GCD (Grand Central Dispatch) care este invocat pe coada principală. Există un aspect cheie al NSURLSession
despre care nu am vorbit încă, invalidarea sesiunii. Sesiunea păstrează o referință puternică la delegatul său, ceea ce înseamnă că delegatul nu este eliberat atâta timp cât sesiunea este activă. Pentru a întrerupe acest ciclu de referință, sesiunea trebuie invalidată. Atunci când o sesiune este invalidată, sarcinile active sunt anulate sau terminate, iar delegatul este trimis a URLSession: didBecomeInvalidWithError:
mesaj și sesiunea îi eliberează pe delegat.
Există mai multe locuri pe care le putem invalida sesiunea. Deoarece controlerul de vizualizare descarcă doar o singură imagine, sesiunea poate fi invalidată la finalizarea descărcării. Uitați-vă la implementarea actualizată a URLSession: downloadTask: didFinishDownloadingToURL:
. Butonul de anulare este, de asemenea, ascuns când se termină descărcarea.
- (void) URLSession: (NSURLSession *) sesiune downloadTask: (NSURLSessionDownloadTask *) downloadTask didFinishDownloadingToURL: (NSURL *) locație NSData * data = [NSData dataWithContentsOfURL: locație]; dispatch_async (dispatch_get_main_queue (), auto.cancelButton setHidden: YES]; [self.progressView setHidden: YES]; [auto.imageView setImage: [UIImage imageWithData: data]];)); // Invalidați sesiunea [session endTasksAndInvalidate];
Proiectul de exemplu pe care l-am creat în acest tutorial este o implementare simplificată a modului de anulare și reluare a descărcărilor. În aplicațiile dvs., poate fi necesar să scrieți resumeData
obiect la disc pentru utilizare ulterioară și este posibil ca mai multe sarcini de descărcare să fie difuzate în același timp. Chiar dacă aceasta adaugă complexitate, principiile de bază rămân aceleași. Asigurați-vă că preveniți scurgeri de memorie prin invalidarea întotdeauna a unei sesiuni pe care nu mai aveți nevoie.