I den forrige opplæringen introduserte jeg deg til NSURLSession
. Jeg snakket om fordelene den har over NSURLConnection
og hvordan du bruker det NSURLSession
for enkle oppgaver, for eksempel å hente data fra en webtjeneste og laste ned et bilde fra nettet. I denne opplæringen tar vi nærmere titt på konfigurasjonsalternativene for NSURLSession
og hvordan du avbryter og gjenopptar en nedlastingsoppgave. Vi har mye grunn til å dekke så la oss komme i gang.
Som vi så i forrige veiledning, en økt, en forekomst av NSURLSession
, er en konfigurerbar beholder for å sette nettverksforespørsler inn i. Konfigurasjonen av økten håndteres av en forekomst av NSURLSessionConfiguration
.
Et øktkonfigurasjonsobjekt er ingenting mer enn en ordbok med egenskaper som definerer hvordan økten den er bundet til å oppføre seg. En økt har ett sesjonskonfigurasjonsobjekt som dikterer informasjon om informasjonskapsler, sikkerhet og buffer, maksimalt antall tilkoblinger til vert, ressurs- og nettverksavbrudd etc. Dette er en betydelig forbedring i forhold til NSURLConnection
, som stod på et globalt konfigurasjonsobjekt med mye mindre fleksibilitet.
Når en økt er opprettet og konfigurert av en NSURLSessionConfiguration
For eksempel kan sesjonens konfigurasjon ikke endres. Hvis du må endre en økts konfigurasjon, må du opprette en ny økt. Husk at det er mulig å kopiere en økts konfigurasjon og endre den, men endringene har ingen effekt på økten der konfigurasjonen ble kopiert.
De NSURLSessionConfiguration
klassen gir tre fabrikkkonstruksjoner for å instansere standardkonfigurasjoner, defaultSessionConfiguration
, ephemeralSessionConfiguration
, og backgroundSessionConfiguration
. Den første metoden returnerer en kopi av standard økt konfigurasjon objekt, noe som resulterer i en økt som oppfører seg på samme måte som en NSURLConnection
objekt i standardkonfigurasjonen. Endre en øktkonfigurasjon oppnådd gjennom defaultSessionConfiguration
Fabrikkmetoden endrer ikke standard sesjonskonfigurasjon som det er en kopi av.
Et økt konfigurasjonsobjekt opprettet ved å påkalle ephemeralSessionConfiguration
Fabrikkmetoden sikrer at den resulterende økten ikke bruker vedvarende lagring for informasjonskapsler, cacher eller legitimasjonsbeskrivelser. Med andre ord, er cookies, cacher og legitimasjon lagret i minnet. Økologiske økter er derfor ideelle hvis du trenger å implementere privat surfing, noe som ikke var mulig før innføringen av NSURLSession
.
De backgroundSessionConfiguration:
fabrikkmetoden oppretter et øktkonfigurasjonsobjekt som gjør det mulig å laste opp og laste ned utenom prosessen. Opplastings- og nedlastingsoppgaver styres av en bakgrunnsdemon og fortsetter å kjøre selv om programmet er suspendert eller krasjer. Vi snakker mer om bakgrunnsøkter senere i denne serien.
Som vi så i den forrige opplæringen, er det enkelt å lage et øktkonfigurasjonsobjekt. I eksemplet som vist nedenfor brukte jeg defaultSessionConfiguration
fabrikk metode for å lage en NSURLSessionConfiguration
forekomst. Konfigurere et øktkonfigurasjonsobjekt er like enkelt som å endre egenskaper som vist i eksemplet. Vi kan deretter bruke sesjonskonfigurasjonsobjektet til å instantiere en øktobjekt. Sessionsobjektet fungerer som en fabrikk for dataopplasting, opplasting og nedlastingsoppgaver, med hver oppgave som svarer til en enkelt forespørsel. I eksemplet nedenfor spør vi iTunes Search API som vi gjorde i den forrige opplæringen.
// Opprett sesjonskonfigurasjon NSURLSessionConfiguration * sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration]; // Konfigurer Session Configuration [SessionConfiguration setAllowsCellularAccess: YES]; [sessionConfiguration setHTTPAdditionalHeaders: @ @ "Accept": @ "application / json"]; // Opprett økt NSURLSession * session = [NSURLSession sessionWithConfiguration: sessionConfiguration]; // Send forespørsel NSURL * url = [NSURL URLWithString: @ "https://itunes.apple.com/search?term=apple&media=software"]; [[session dataTaskWithURL: url completionHandler: ^ (NSData * data, NSURLResponse * respons, NSError * feil) NSLog (@ "% @", [NSJSONSerialization JSONObjectWithData: dataalternativer: 0 feil: null]); ] gjenoppta];
Eksemplet illustrerer også hvor enkelt det er å legge til egendefinerte overskrifter ved å sette inn HTTPAdditionalHeaders
egenskapen til sesjonskonfigurasjonsobjektet. Skjønnheten i NSURLSession
API er at hver forespørsel som går gjennom økten, er konfigurert av sesjonens konfigurasjonsobjekt. Hvis du legger til autentiseringshoder til et sett med forespørsler, blir det for eksempel lett som kake.
I den forrige veiledningen viste jeg deg hvordan du laster ned et bilde ved hjelp av NSURLSession
API. Nettverksforbindelser er imidlertid upålitelige, og det skjer altfor ofte at en nedlasting mislykkes på grunn av en flakket nettverkstilkobling. Heldigvis gjenoppta en nedlasting er ikke vanskelig med NSURLSession
API. I det neste eksemplet viser jeg deg hvordan du avbryter og fortsetter nedlastingen av et bilde.
Før vi tar en nærmere titt på å gjenoppta en nedlastingsoppgave, er det viktig å forstå forskjellen mellom å kansellere og suspendere en nedlastingsoppgave. Det er mulig å suspendere en nedlastingsoppgave og gjenoppta den på et senere tidspunkt. Avbryter en nedlastingsoppgave, men stopper oppgaven, og det er ikke mulig å gjenoppta det på et senere tidspunkt. Det er imidlertid ett alternativ. Det er mulig å avbryte en nedlastingsoppgave ved å ringe cancelByProducingResumeData:
på den. Den aksepterer en ferdigstillingshåndterer som aksepterer en parameter, en NSData
objekt som brukes til å gjenoppta nedlastingen på et senere tidspunkt ved å påkalle downloadTaskWithResumeData:
eller downloadTaskWithResumeData: completionHandler:
på et økt objekt. De NSData
objekt inneholder nødvendig informasjon for å gjenoppta nedlastingsoppgaven der den sluttet.
Åpne prosjektet vi opprettet i den forrige opplæringen eller last den ned her. Vi starter med å legge to knapper til brukergrensesnittet, en for å avbryte nedlastingen og en for å gjenoppta nedlastingen. I tittelkontrollens headerfil lager du et uttak og en handling for hver knapp som vist nedenfor.
#importere@interface MTViewController: UIViewController @property (svak, ikkeatomisk) IBOutlet UIButton * CancelButton; @property (svak, ikkeatomisk) IBOutlet UIButton * resumeButton; @property (svak, ikkeatomisk) IBOutlet UIImageView * imageView; @property (svak, ikkeatomisk) IBOutlet UIProgressView * progressView; - (IBAction) avbryt: (id) avsender; - (IBAction) CV: (id) avsender; @slutt
Åpne prosjektets hoved storyboard og legg til to knapper til visningskontrollørens visning. Plasser knappene som vist på skjermbildet under og koble hver knapp med tilhørende uttak og handling.
Vi må gjøre noe for å få alt til å fungere riktig. Åpen MTViewController.m
og erklære en instansvariabel og to egenskaper. Instansvariabelen, økt
, vil holde en referanse til økten vi skal bruke for å laste ned bildet.
#import "MTViewController.h" @interface MTViewController ()NSURLSession * _session; @property (strong, nonatomic) NSURLSessionDownloadTask * downloadTask; @property (strong, nonatomic) NSData * resumeData; @slutt
Vi må også refactor the viewDidLoad
metode, men først vil jeg gjerne implementere en getter-metode for økten. Implementeringen er ganske enkel som du kan se nedenfor. Vi lager et sesjonskonfigurasjonsobjekt ved hjelp av defaultSessionConfiguration
fabrikkmetode og instansere øktobjektet med det. Visningsregulatoren fungerer som øktens delegat.
- (NSURLSession *) økt if (! _Session) // Opprett sesjonskonfigurasjon NSURLSessionConfiguration * sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration]; // Opprett økt _session = [NSURLSession sessionWithConfiguration: sessionConfiguration delegate: self delegateQueue: null]; tilbake _session;
Med økt
Accessor implementert, viewDidLoad
Metoden blir mye enklere. Vi lager en nedlastingsoppgave, som vi gjorde i den forrige opplæringen, og lagrer en referanse til oppgaven i downloadTask
. Vi forteller deretter nedlastingsoppgaven til gjenoppta
.
- (void) viewDidLoad [super viewDidLoad]; // Opprett nedlastingsoppgave self.downloadTask = [self.session downloadTaskWithURL: [NSURL URLWithString: @ "http://cdn.tutsplus.com/mobile/uploads/2014/01/5a3f1-sample.jpg"]]; // Fortsett nedlastingsoppgave [self.downloadTask gjenoppta];
De Avbryt:
Handling inneholder logikken for å kansellere nedlastingsoppgaven vi nettopp har opprettet. Hvis downloadTask
er ikke nil
, vi ringer cancelByProducingResumeData:
på oppgaven. Denne metoden aksepterer en parameter, en ferdigstillingsblokk. Fullføringsblokken tar også en parameter, en forekomst av NSData
. Hvis resumeData
er ikke nil
, Vi lagrer en referanse til dataobjektet i kontrolleren resumeData
eiendom.
Hvis en nedlastning ikke gjenopptas, er ferdigstillingsblokken resumeData
parameteren er nil
. Ikke alle nedlastinger gjenopptas, så det er viktig å sjekke om resumeData
er gyldig NSData
gjenstand.
- (IBAction) avbryt: (id) avsender hvis (! Self.downloadTask) returnerer; // Skjul Avbryt-knapp [self.cancelButton setHidden: YES]; [self.downloadTask cancelByProducingResumeData: ^ (NSData * resumeData) hvis (! resumeData) returnerer; [self setResumeData: resumeData]; [self setDownloadTask: null]; ];
Det er enkelt å gjenoppta nedlastingsoppgaven etter at den ble kansellert. I gjenoppta:
handling, vi sjekker om visningskontrolleren er resumeData
eiendommen er satt. Hvis resumeData
er gyldig NSData
objekt, forteller vi økt
motsette seg å opprette en ny nedlastingsoppgave og sende den til NSData
gjenstand. Dette er alt økt
objektet må gjenskape nedlastingsoppgaven vi avbrutt i Avbryt:
handling. Vi forteller deretter nedlastingsoppgaven til gjenoppta
og sett resumeData
til nil
.
- (IBAction) CV: (id) avsender hvis (! Self.resumeData) returnerer; // Skjul CV-knappen [self.resumeButton setHidden: YES]; // Opprett nedlastingsoppgave self.downloadTask = [self.session downloadTaskWithResumeData: self.resumeData]; // Fortsett nedlastingsoppgave [self.downloadTask gjenoppta]; // Opprydding [self setResumeData: null];
Bygg prosjektet og kjør programmet i iOS-simulatoren eller på en fysisk enhet. Nedlastingen skal starte automatisk. Trykk på Avbryt-knappen for å avbryte nedlastingen og trykk på Fortsett-knappen for å gjenoppta nedlastingen.
Det er en rekke detaljer vi må ta vare på. Først og fremst bør knappene ikke alltid være synlige. Vi bruker nøkkelverdien å observere for å vise og skjule knappene når det er nødvendig. I viewDidLoad
, skjul knappene og legg til visningskontrollen som en observatør for seg selv for resumeData
og downloadTask
viktige stier.
- (void) viewDidLoad [super viewDidLoad]; // Legg til observatør [self addObserver: self forKeyPath: @ "resumeData" alternativer: NSKeyValueObservingOptionNew context: NULL]; [self addObserver: self forKeyPath: @ "downloadTask" alternativer: NSKeyValueObservingOptionNew kontekst: NULL]; // Oppsett brukergrensesnitt [self.cancelButton setHidden: YES]; [self.resumeButton setHidden: YES]; // Opprett nedlastingsoppgave self.downloadTask = [self.session downloadTaskWithURL: [NSURL URLWithString: @ "http://cdn.tutsplus.com/mobile/uploads/2014/01/5a3f1-sample.jpg"]]; // Fortsett nedlastingsoppgave [self.downloadTask gjenoppta];
I observeValueForKeyPath: ofObject: endring: kontekst:
, vi skjuler avbryt-knappen hvis resumeData
er nil
og vi skjuler CV-knappen hvis downloadTask
er nil
. Bygg prosjektet og kjør programmet enda en gang for å se resultatet. Dette er bedre. Ikke sant?
- (void) observeValueForKeyPath: (NSString *) keyPath ofObject: (id) objektendring: (NSDictionary *) endre kontekst: (void *) kontekst if ([keyPath isEqualToString: @ "resumeData"]) dispatch_async (dispatch_get_main_queue ^ [self.resumeButton setHidden: (self.resumeData == nil)];); ellers hvis ([keyPath isEqualToString: @ "downloadTask"]) dispatch_async (dispatch_get_main_queue (), ^ [self.cancelButton setHidden: (self.downloadTask == nil)];);Som George Yang påpeker i kommentarene, vet vi ikke om
observeValueForKeyPath: ofObject: endring: kontekst:
kaller på hovedtråden. Det er derfor viktig å oppdatere brukergrensesnittet i en GCD (Grand Central Dispatch) -blokk som er påkalt på hovedkøen. Det er et viktig aspekt av NSURLSession
som jeg ikke har snakket om ennå, økt ugyldighet. Økten holder en sterk referanse til sin delegat, noe som betyr at delegaten ikke slippes så lenge sesjonen er aktiv. For å bryte denne referansesyklusen må sesjonen ugyldiggjøres. Når en økt er ugyldig, blir aktive oppgaver kansellert eller ferdig, og delegaten sendes a URLSession: didBecomeInvalidWithError:
melding og økten utgir sin delegat.
Det er flere steder vi kan ugyldiggjøre økten. Siden visningskontrollen laster ned bare ett bilde, kan sesjonen ugyldiggjøres når nedlastingen er ferdig. Ta en titt på den oppdaterte implementeringen av URLSession: downloadTask: didFinishDownloadingToURL:
. Avbryt-knappen er også skjult når nedlastingen er ferdig.
- (void) URLSession: (NSURLSession *) sessionsnedlastningTask: (NSURLSessionDownloadTask *) downloadTask didFinishDownloadingToURL: (NSURL *) sted NSData * data = [NSData dataWithContentsOfURL: location]; dispatch_async (dispatch_get_main_queue (), ^ self.cancelButton setHidden: YES]; [self.progressView setHidden: YES]; [self.imageView setImage: [UIImage imageWithData: data]];); // Invalidiser økt [Session finishTasksAndInvalidate];
Eksemplet prosjektet vi opprettet i denne opplæringen er en forenklet implementering av hvordan du avbryter og gjenopptar nedlastinger. I dine applikasjoner kan det være nødvendig å skrive resumeData
Motta disk for senere bruk, og det kan være mulig at flere nedlastingsoppgaver kjører samtidig. Selv om dette legger til kompleksitet, forblir de grunnleggende prinsippene de samme. Pass på at du forhindrer hukommelse ved å alltid ugyldiggjøre en økt som du ikke lenger trenger.