iOS 8 Date de bază și preluare asincronă

În articolul precedent despre iOS 8 și Core Data, am discutat despre actualizări batch. Actualizările batch nu sunt singurele API noi în oraș. Începând cu iOS 8 și OS X Yosemite, este posibilă preluarea asincronă a datelor. În acest tutorial, vom examina mai detaliat modul de implementare a preluării asincrone și în ce situații aplicația dvs. poate beneficia de acest nou API.

1. Problema

Ca actualizări batch, preluarea asincronă a fost pe lista de dorințe a multor dezvoltatori de ceva timp. Solicitările de solicitare pot fi complexe, luând o perioadă netrivială de timp pentru a le finaliza. În acest timp, solicitarea de preluare blochează firul pe care rulează și, ca rezultat, blochează accesul la contextul obiect gestionat executând solicitarea de preluare. Problema este ușor de înțeles, dar cum arăta soluția Apple.

2. Soluția

Răspunsul Apple la această problemă este preluarea asincronă. O cerere de preluare asincronă rulează în fundal. Aceasta înseamnă că nu blochează alte sarcini în timp ce acestea sunt executate, cum ar fi actualizarea interfeței utilizator pe firul principal.

Executarea asincronează și alte două caracteristici convenabile, raportarea progresului și anularea. O cerere de preluare asincronă poate fi anulată în orice moment, de exemplu, atunci când utilizatorul decide că solicitarea de preluare durează prea mult timp până la finalizare. Raportarea progreselor este un plus util pentru a arăta utilizatorului starea actuală a solicitării de preluare.

Preluarea asincronă este un API flexibil. Nu este posibilă numai anularea unei solicitări de preluare asincronă, dar este posibilă și modificarea contextului obiect gestionat în timp ce cererea de preluare asincronă este executată. Cu alte cuvinte, utilizatorul poate continua să utilizeze aplicația în timp ce aplicația execută o cerere de preluare asincronă în fundal.

3. Cum funcționează?

Ca actualizări batch, cererile de preluare asincronă sunt transmise contextului gestionat al obiectului ca un NSPersistentStoreRequest obiect, o instanță a NSAsynchronousFetchRequest pentru a fi precis.

Un NSAsynchronousFetchRequest instanta este initializata cu un NSFetchRequest obiect și un bloc de completare. Blocul de completare este executat atunci când cererea de preluare asincronă a finalizat solicitarea de preluare.

Să revizuim aplicația de creare pe care am creat-o mai devreme în această serie și să înlocuim implementarea actuală a NSFetchedResultsController clasă cu o cerere de preluare asincronă.

Pasul 1: Configurarea proiectului

Descărcați sau clonați proiectul de la GitHub și deschideți-l în Xcode 6. Înainte de a începe să lucrați cu NSAsynchronousFetchRequest clasa, trebuie să facem unele schimbări. Nu vom putea folosi NSFetchedResultsController pentru gestionarea datelor din tabelul de vizualizare de la NSFetchedResultsController clasa a fost proiectată să funcționeze pe firul principal.

Pasul 2: Înlocuirea controlerului de rezultate preluate

Începeți prin actualizarea extensiei de clasă privată a TSPViewController clasa, după cum se arată mai jos. Eliminăm fetchedResultsController de proprietate și de a crea o proprietate nouă, articole, de tip NSArray pentru stocarea articolelor de rezolvat. Acest lucru înseamnă, de asemenea, că TSPViewController clasa nu mai trebuie să se conformeze NSFetchedResultsControllerDelegate protocol.

@interface TSPViewController () @property (puternic, nonatomic) NSArray * elemente; @property (puternic, nonatomic) NSIndexPath * selecție; @Sfârșit

Înainte de a refactoriza viewDidLoad metoda, doresc mai întâi să actualizeze punerea în aplicare a UITableViewDataSource protocol. Uitați-vă la schimbările pe care le-am făcut în următoarele blocuri de coduri.

- (NSInteger) numberOfSectionsInTableView: (UITableView *) tableView return self.items? 1: 0; 
- (NSInteger) tableView: (UITableView *) tableView numberOfRowsInSection: (NSInteger) sectiunea return self.items? auto.items.count: 0; 
- (void) configureCell: (TSPToDoCell *) celula atIndexPath: (NSIndexPath *) indexPath / / Fetch Record NSManagedObject * record = [auto.items objectAtIndex: indexPath.row]; // Actualizează celula [cell.nameLabel setText: [record valueForKey: @ "nume"]]; [cell.doneButton setSelected: [[valoare recordForKey: @ "done"] boolValue]]; [celulă setDidTapButtonBlock: ^ BOOL isDone = [[valoare recordForKey: @ "făcut"] boolValue]; // Actualizați înregistrarea [record setValue: @ (! IsDone) pentruKey: @ "done"]; ]; 
- (void) tableView: (UITableView *) tableView commitEditingStyle: (UITableViewCellEditingStyle) editareStyle forRowAtIndexPath: (NSIndexPath *) indexPath if (editingStyle ==UITableViewCellEditingStyleDelete) NSManagedObject * record = [auto.items objectAtIndex: indexPath.row]; dacă (înregistra) [auto.managedObjectContext deleteObject: record]; 

De asemenea, trebuie să schimbăm o linie de cod în prepareForSegue: expeditor: așa cum se arată mai jos.

// Recuperare înregistrare NSManagedObject * record = [auto.items objectAtIndex: self.selection.row];

Nu în ultimul rând, ștergeți implementarea NSFetchedResultsControllerDelegate deoarece nu mai avem nevoie de ea.

Pasul 3: Crearea solicitării de preluare asincronă

După cum puteți vedea mai jos, vom crea solicitarea de preluare asincronă în controlerul de vizualizare viewDidLoad metodă. Să luăm o clipă pentru a vedea ce se întâmplă.

- (vid) viewDidLoad [super viewDidLoad]; // helpers __weak TSPViewController * slabSelf = auto; // Inițializați solicitarea de preluare NSFetchRequest * fetchRequest = [[NSFetchRequest alocați] initWithEntityName: @ "TSPItem"]; // Adăugați descriptori de sortare [fetchRequest setSortDescriptors: @ [[NSSortDescriptor sortDescriptorWithKey: @ "createdAt" ascendent: YES]]]; // Initializeaza solicitarea asincronei de preluare NSAsynchronousFetchRequest * asynchronousFetchRequest = [[NSAsynchronousFetchRequest alocare] initWithFetchRequest: fetchRequest completionBlock: ^ (NSAsynchronousFetchResult * rezultat) dispatch_async (dispatch_get_main_queue (), ^ // Rezolvarea procesului asincron de preluare [slaveSistem de procesareAsynchronousFetchResult: result; ; ]; // Executați solicitarea asincronă de preluare [self.managedObjectContext performBlock: ^ // Executați solicitarea asincronă de preluare NSError * asynchronousFetchRequestError = nil; NSAsynchronousFetchResult * asynchronousFetchResult = (NSAsynchronousFetchResult *) [slabăSman.managedObjectContext executeRequest: asynchronousFetchRequest error: & asynchronousFetchRequestError]; if (asynchronousFetchRequestError) NSLog (@ "Nu se poate executa rezultatul preluării asincrone."); NSLog (@ "% @,% @", asincronăFetchRequestError, asynchronousFetchRequestError.localizedDescription); ]; 

Începem prin crearea și configurarea unui NSFetchRequest instanță pentru a inițializa solicitarea de preluare asincronă. Este această solicitare de preluare ca cererea de preluare asincronă să se execute în fundal.

// Inițializați solicitarea de preluare NSFetchRequest * fetchRequest = [[NSFetchRequest alocați] initWithEntityName: @ "TSPItem"]; // Adăugați descriptori de sortare [fetchRequest setSortDescriptors: @ [[NSSortDescriptor sortDescriptorWithKey: @ "createdAt" ascendent: YES]]];

Pentru a inițializa un NSAsynchronousFetchRequest exemplu, invocăm initWithFetchRequest: completionBlock:, trecerea fetchRequest și un bloc de completare.

// Initializeaza solicitarea asincronei de preluare NSAsynchronousFetchRequest * asynchronousFetchRequest = [[NSAsynchronousFetchRequest alocare] initWithFetchRequest: fetchRequest completionBlock: ^ (NSAsynchronousFetchResult * rezultat) dispatch_async (dispatch_get_main_queue (), ^ // Rezolvarea procesului asincron de preluare [slaveSistem de procesareAsynchronousFetchResult: result; ; ];

Blocul de completare este invocat când cererea de preluare asincronă a finalizat executarea cererii sale de preluare. Blocul de completare are un argument de tip NSAsynchronousFetchResult, care conține rezultatul interogării, precum și o trimitere la cererea inițială de preluare asincronă.

În blocul de completare, invocăm processAsynchronousFetchResult:, trecerea în NSAsynchronousFetchResult obiect. Vom analiza această metodă de ajutor în câteva momente.

Executarea cererii de preluare asincronă este aproape identică cu cea pe care o executăm NSBatchUpdateRequest. Noi sunam executeRequest: eroare: pe contextul obiectului gestionat, trecând în cererea de preluare asincronă și un pointer la un NSError obiect.

[self.managedObjectContext performBlock: ^ // Executați solicitarea asincronă de preluare NSError * asynchronousFetchRequestError = nil; NSAsynchronousFetchResult * asynchronousFetchResult = (NSAsynchronousFetchResult *) [slabăSman.managedObjectContext executeRequest: asynchronousFetchRequest error: & asynchronousFetchRequestError]; if (asynchronousFetchRequestError) NSLog (@ "Nu se poate executa rezultatul preluării asincrone."); NSLog (@ "% @,% @", asincronăFetchRequestError, asynchronousFetchRequestError.localizedDescription); ];

Rețineți că executăm solicitarea de preluare asincronă apelând performBlock: pe contextul obiect gestionat. Deși acest lucru nu este strict necesar din moment ce viewDidLoad în care se creează și se execută cererea de preluare asincronă, se cheamă pe firul principal, este un bun obicei și cele mai bune practici pentru a face acest lucru.

Chiar dacă cererea de preluare asincronă este executată în fundal, rețineți că executeRequest: eroare: metoda se întoarce imediat, predându-ne o NSAsynchronousFetchResult obiect. Odată ce cererea de preluare asincronă se încheie, același lucru NSAsynchronousFetchResult obiect este populate cu rezultatul solicitării de preluare.

În cele din urmă, verificăm dacă cererea de preluare asincronă a fost executată fără probleme, verificând dacă NSError obiect este egal cu zero.

Pasul 4: Prelucrarea rezultatului asincron de preluare

 processAsynchronousFetchResult: metoda nu este altceva decât o metodă de ajutor în care procesăm rezultatul cererii de preluare asincronă. Am setat controlerul de vizualizare articole proprietate cu conținutul rezultatelor rezultat final proprietăți și reîncărcați vizualizarea tabelului.

- (void) processAsynchronousFetchResult: (NSAsynchronousFetchResult *) asincronăFetchResult if (asynchronousFetchResult.finalResult) // Update Items [auto setItems: asynchronousFetchResult.finalResult]; // Reîncarcă tabelul [self.tableView reloadData]; 

Pasul 5: Construiți și executați

Construiți proiectul și rulați aplicația în Simulatorul iOS. S-ar putea să fiți surprins să vedeți căderea aplicației dvs. atunci când încearcă să execute cererea de preluare asincronă. Din fericire, ieșirea din consola ne spune ce a mers prost.

*** Terminarea aplicației din cauza excepției "NSInvalidArgumentException", motiv: "NSConfinementConcurrencyType context  nu poate accepta solicitarea de preluare asincronă  cu solicitare de preluare  (entitate: TSPItem; predicat: ((null)); sortDescriptors: (("(createAt, ascending, compare :)")), type: NSManagedObjectResultType;

Dacă nu ați citit articolul despre Datele de bază și despre concurență, este posibil să fiți confundați cu ceea ce citiți. Rețineți că datele de bază declară trei tipuri de concurență, NSConfinementConcurrencyTypeNSPrivateQueueConcurrencyType, și NSMainQueueConcurrencyType. Ori de câte ori creați un context de obiect gestionat prin invocarea clasei init , tipul de concurrency al contextului obiect gestionat rezultat este egal cu NSConfinementConcurrencyType. Acesta este tipul implicit de concurență.

Problema, totuși, este că preluarea asincronă este incompatibilă cu NSConfinementConcurrencyType tip. Fără a intra în prea multe detalii, este important să știm că cererea de preluare asincronă trebuie să îmbine rezultatele cererii sale de preluare cu contextul obiect gestionat care a executat cererea de preluare asincronă. Trebuie să știe pe ce coadă de expediere poate face acest lucru și de aceea numai NSPrivateQueueConcurrencyType și NSMainQueueConcurrencyType sprijiniți preluarea asincronă. Soluția este însă foarte simplă.

Pasul 6: Configurarea contextului de obiect gestionat

Deschis TSPAppDelegate.m și actualizați managedObjectContext așa cum se arată mai jos.

- (NSManagedObjectContext *) managedObjectContext if (_managedObjectContext) returnați _managedObjectContext;  NSPersistentStoreCoordinator * coordonator = [auto persistentStoreCoordinator]; dacă (coordonator) _managedObjectContext = [[NSManagedObjectContext alin] initWithConcurrencyType: NSMainQueueConcurrencyType]; [_managedObjectContext setPersistentStoreCoordinator: coordonator];  returnați _managedObjectContext; 

Singura schimbare pe care am făcut-o este înlocuirea init metoda cu initWithConcurrencyType:, trecerea NSMainQueueConcurrencyType ca argument. Aceasta înseamnă că contextul obiect gestionat trebuie accesat numai din firul principal. Acest lucru funcționează bine atâta timp cât folosim performBlock: sau performBlockAndWait: metode pentru a accesa contextul obiect gestionat.

Executați încă o dată proiectul pentru a vă asigura că schimbarea noastră a rezolvat într-adevăr problema.

4. Afișarea progresului

NSAsynchronousFetchRequest clasa adaugă suport pentru monitorizarea progresului solicitării de preluare și este chiar posibil să anulați o cerere de preluare asincronă, de exemplu, dacă utilizatorul decide că durează prea mult pentru a finaliza.

 NSAsynchronousFetchRequest clasa utilizează NSProgress clasă pentru raportarea progresului, precum și anularea unei solicitări de preluare asincronă. NSProgress clasa, disponibilă de la iOS 7 și OS X 10.9, este un mod inteligent de a monitoriza progresul unei sarcini fără a fi nevoie să cuplați strâns sarcina la interfața cu utilizatorul.

NSProgress de clasă, de asemenea, suporta anularea, care este modul în care o cerere de preluare asincronă poate fi anulată. Să aflăm ce trebuie să facem pentru a implementa raportarea progresului pentru solicitarea de preluare asincronă.

Pasul 1: Adăugarea SVProgressHUD

Vom arăta utilizatorului evoluția cererii de preluare asincronă utilizând biblioteca SVProgressHUD a lui Sam Vermette. Descărcați biblioteca de la GitHub și adăugați-o SVProgressHUD folder pentru proiectul dvs. Xcode.

Pasul 2: Configurarea NSProgress 

În acest articol, nu vom explora NSProgress clasa în detaliu, dar nu ezitați să citiți mai multe despre aceasta în documentație. Noi creăm un NSProgress de exemplu, în blocul pe care îl transmitem performBlock: metodă în controlerul de vizualizare viewDidLoad metodă.

// Crearea progresului NSProgress * progress = [NSProgress progressWithTotalUnitCount: 1]; // deveni curent [progress becomeCurrentWithPendingUnitCount: 1];

S-ar putea să vă surprindeți că am setat numărul total de unități la 1. Motivul este simplu. Când datele de bază execută cererea de preluare asincronă, nu știe câte înregistrări vor găsi în magazinul persistent. Acest lucru înseamnă, de asemenea, că nu vom putea prezenta progresul relativ la utilizator - un procentaj. În schimb, vom arăta utilizatorului progresul absolut - numărul de înregistrări pe care le-a găsit.

Ați putea remedia această problemă efectuând o solicitare de preluare pentru a prelua numărul de înregistrări înainte de a executa solicitarea de preluare asincronă. Prefer să nu fac acest lucru, totuși, deoarece acest lucru înseamnă, de asemenea, că preluarea înregistrărilor din magazinul persistent durează mai mult pentru a fi terminată din cauza solicitării suplimentare de preluare la început.

Pasul 3: Adăugarea unui observator

Când executăm cererea de preluare asincronă, suntem imediat înmânate NSAsynchronousFetchResult obiect. Acest obiect are a progres proprietate, care este de tip NSProgress. Este asta progres proprietate pe care trebuie să o respectăm dacă vrem să primim actualizări de progres.

// Executați solicitarea asincronă de preluare [self.managedObjectContext performBlock: ^ // Creare progres NSProgress * progress = [NSProgress progressWithTotalUnitCount: 1]; // deveni curent [progress becomeCurrentWithPendingUnitCount: 1]; // Executați solicitarea asincronă de preluare NSError * asynchronousFetchRequestError = nil; NSAsynchronousFetchResult * asynchronousFetchResult = (NSAsynchronousFetchResult *) [auto.managedObjectContext executeRequest: eroare asynchronousFetchRequest: & asynchronousFetchRequestError]; if (asynchronousFetchRequestError) NSLog (@ "Nu se poate executa rezultatul preluării asincrone."); NSLog (@ "% @,% @", asincronăFetchRequestError, asynchronousFetchRequestError.localizedDescription);  // Adăugați Observer [asynchronousFetchResult.progress addObserver: self forKeyPath: @ "completedUnitCount" opțiuni: NSKeyValueObservingOptionNew context: ProgressContext]; // Demisia curentă [progress resignCurrent]; ];

Rețineți că sunăm resignCurrent pe progres obiect pentru a echilibra mai devreme becomeCurrentWithPendingUnitCount: apel. Rețineți că ambele metode trebuie invocate pe același fir.

Pasul 4: Înlăturarea observatorului

În blocul de completare a cererii de preluare asincronă, eliminăm observatorul și respingem progresul HUD.

// Initializeaza solicitarea asincronei de preluare NSAsynchronousFetchRequest * asynchronousFetchRequest = [[NSAsynchronousFetchRequest alocare] initWithFetchRequest: fetchRequest completionBlock: ^ (NSAsynchronousFetchResult * rezultat) dispatch_async (dispatch_get_main_queue (), ^ // respinge HUD Progress [SVProgressHUD dismiss] Rezultat [proces slabSynchronousFetchResult: rezultat]; // Remove Observer [result.progress removeObserver: weakSelf forKeyPath: @ "completedUnitCount" context: ProgressContext];); ];

Înainte să implementăm observeValueForKeyPath: ofObject: schimbare: Context:, trebuie să adăugăm o declarație de import pentru biblioteca SVProgressHUD, să declare variabila statică ProgressContext pe care le transmitem în contextul adăugării și eliminării observatorului, și vom arăta progresul HUD înainte de a crea solicitarea de preluare asincronă.

#import "SVProgressHUD / SVProgressHUD.h"
static void * ProgressContext = & ProgressContext;
- (vid) viewDidLoad [super viewDidLoad]; // helpers __weak TSPViewController * slabSelf = auto; // Afișați progresul HUD [SVProgressHUD showWithStatus: @ "Data preluării" maskType: SVProgressHUDMaskTypeGradient]; // Inițializați solicitarea de preluare NSFetchRequest * fetchRequest = [[NSFetchRequest alocați] initWithEntityName: @ "TSPItem"]; // Adăugați descriptori de sortare [fetchRequest setSortDescriptors: @ [[NSSortDescriptor sortDescriptorWithKey: @ "createdAt" ascendent: YES]]]; // Initializeaza solicitarea asincronei de preluare NSAsynchronousFetchRequest * asynchronousFetchRequest = [[NSAsynchronousFetchRequest alocare] initWithFetchRequest: fetchRequest completionBlock: ^ (NSAsynchronousFetchResult * rezultat) dispatch_async (dispatch_get_main_queue (), ^ // respinge HUD Progress [SVProgressHUD dismiss] Rezultat [proces slabSynchronousFetchResult: rezultat]; // Remove Observer [result.progress removeObserver: weakSelf forKeyPath: @ "completedUnitCount" context: ProgressContext];); ]; // Executați solicitarea asincronă de preluare [self.managedObjectContext performBlock: ^ // Creare progres NSProgress * progress = [NSProgress progressWithTotalUnitCount: 1]; // deveni curent [progress becomeCurrentWithPendingUnitCount: 1]; // Executați solicitarea asincronă de preluare NSError * asynchronousFetchRequestError = nil; NSAsynchronousFetchResult * asynchronousFetchResult = (NSAsynchronousFetchResult *) [slabăSman.managedObjectContext executeRequest: asynchronousFetchRequest error: & asynchronousFetchRequestError]; if (asynchronousFetchRequestError) NSLog (@ "Nu se poate executa rezultatul preluării asincrone."); NSLog (@ "% @,% @", asincronăFetchRequestError, asynchronousFetchRequestError.localizedDescription);  // Adăugați Observer [asynchronousFetchResult.progress addObserver: self forKeyPath: @ "completedUnitCount" opțiuni: NSKeyValueObservingOptionNew context: ProgressContext]; // Demisia curentă [progress resignCurrent]; ]; 

Pasul 5: Raportarea progresului

Tot ce a mai rămas pentru noi, este implementarea observeValueForKeyPath: ofObject: schimbare: Context: metodă. Verificăm dacă context este egal cu ProgressContext, creeaza o stare obiect prin extragerea numărului de înregistrări completate de la Schimbare dicționar și actualizați progresul HUD. Rețineți că actualizăm interfața de utilizator pe firul principal.

- (void) observeValueForKeyPath: (NSString *) cheiePath ofObject: (id) schimbare obiect: (NSDictionary *) schimbare context: (void *) context if (context == ProgressContext) dispatch_async (dispatch_get_main_queue Stare NSString * status = [NSString stringWithFormat: @ "Retinut% li Record", (lung) [[schimbare objectForKey: @ "new"] integerValue]; 

5. Date Dummy

Dacă dorim să testați în mod corespunzător aplicația noastră, avem nevoie de mai multe date. În timp ce nu recomand utilizarea următoarei abordări într-o aplicație de producție, este o modalitate rapidă și ușoară de a popula baza de date cu date.

Deschis TSPAppDelegate.m și actualizați aplicare: didFinishLaunchingWithOptions: așa cum se arată mai jos. populateDatabase metoda este o metodă simplă de ajutor în care adăugăm date fictive în baza de date.

- (BOOL) aplicație: (UIApplication *) cerere didFinishLaunchingWithOptions: (NSDictionary *) launchOptions // Populate Database [auto populateDatabase]; 

Implementarea este simplă. Pentru că nu vrem decât să inserăm o dată datele fictive, verificăm baza de date implicită pentru utilizator @ "DidPopulateDatabase". Dacă cheia nu este setată, vom introduce datele fictive.

- (void) populateDatabase // Helpers NSUserDefaults * ud = [NSUserDefaults standardUserDefaults]; dacă ([ud objectForKey: @ "didPopulateDatabase"]) retur; pentru (NSInteger i = 0; i < 1000000; i++)  // Create Entity NSEntityDescription *entity = [NSEntityDescription entityForName:@"TSPItem" inManagedObjectContext:self.managedObjectContext]; // Initialize Record NSManagedObject *record = [[NSManagedObject alloc] initWithEntity:entity insertIntoManagedObjectContext:self.managedObjectContext]; // Populate Record [record setValue:[NSString stringWithFormat:@"Item %li", (long)i] forKey:@"name"]; [record setValue:[NSDate date] forKey:@"createdAt"];  // Save Managed Object Context [self saveManagedObjectContext]; // Update User Defaults [ud setBool:YES forKey:@"didPopulateDatabase"]; 

 Numărul de înregistrări este important. Dacă intenționați să rulați aplicația pe Simulatorul iOS, atunci este bine să introduceți 100.000 sau 1.000.000 de înregistrări. Acest lucru nu va funcționa la fel de bine pe un dispozitiv fizic și va dura prea mult timp până la finalizare.

În pentru buclă, vom crea un obiect gestionat și îl vom ocupa cu date. Rețineți că nu salvăm modificările contextului de obiecte gestionate în timpul fiecarei iterații a pentru buclă.

În cele din urmă, actualizăm baza de date implicită a utilizatorilor pentru a ne asigura că baza de date nu este populată data viitoare când este lansată aplicația.

Grozav. Rulați aplicația în Simulatorul iOS pentru a vedea rezultatul. Veți observa că este nevoie de câteva momente pentru solicitarea de preluare asincronă pentru a începe să preluați înregistrările și să actualizați progresul HUD.

6. Schimbarea schimbărilor

Prin înlocuirea clasei controlorilor de rezultate preluate cu o cerere de preluare asincronă, am rupt câteva fragmente din aplicație. De exemplu, apăsarea semnei de selectare a unui element de rezolvat nu pare să funcționeze mai mult. În timp ce baza de date este în curs de actualizare, interfața cu utilizatorul nu reflectă schimbarea. Soluția este destul de ușor de rezolvat și vă voi lăsa la latitudinea dvs. să implementați o soluție. Acum ar trebui să aveți suficiente cunoștințe pentru a înțelege problema și pentru a găsi o soluție potrivită.

Concluzie

Sunt sigur că sunteți de acord că preluarea asincronă este surprinzător de ușor de utilizat. Ridicarea greoaie se face de către datele de bază, ceea ce înseamnă că nu este nevoie să îmbinați manual rezultatele cererii de preluare asincronă cu contextul obiect gestionat. Singura dvs. sarcină este să actualizați interfața de utilizator atunci când cererea de preluare asincronă vă dă rezultate. Împreună cu actualizările batch, este o completare extraordinară a cadrului Core Data.

Acest articol încheie, de asemenea, această serie despre Core Data. Ați învățat multe despre cadrul Core Data și cunoașteți toate elementele esențiale pentru a utiliza datele de bază într-o aplicație reală. Datele de bază reprezintă un cadru puternic și, odată cu lansarea iOS 8, Apple ne-a arătat că devine mai bine în fiecare an.

Cod