iOS 8 Actualizări de date de bază și loturi

Datele de bază au fost în jur de mulți ani pe OS X și nu au luat mult timp Apple să o aducă la iOS. Chiar dacă cadrul nu primește atât de multă atenție ca și extensiile sau transferurile, acesta continuă să evolueze de la an la an, iar anul acesta, odată cu lansarea iOS 8 și OS X Yosemite, nu este diferit.

Apple a introdus câteva caracteristici noi în cadrul Core Data, dar cele mai notabile sunt actualizările batch și preluarea asincronă. Dezvoltatorii au solicitat aceste caracteristici de mai mulți ani, iar Apple a găsit în sfârșit o modalitate de a le integra în Core Data. În acest tutorial, vă voi arăta cum funcționează actualizările batch și ce înseamnă acestea pentru cadrul Core Data.

1. Problema

Datele de bază sunt excelente în gestionarea graficelor de obiecte. Chiar și graficele de obiecte complexe cu multe entități și relații nu reprezintă o problemă majoră pentru datele de bază. Cu toate acestea, datele de bază au câteva puncte slabe, iar actualizarea unui număr mare de înregistrări este una dintre ele.

Problema este ușor de înțeles. Ori de câte ori actualizați o înregistrare, Core Data încarcă înregistrarea în memorie, actualizează înregistrarea și salvează modificările în magazinul persistent, de exemplu o bază de date SQLite.

Dacă datele de bază au nevoie să actualizeze un număr mare de înregistrări, trebuie să încarce fiecare înregistrare în memorie, să actualizeze înregistrarea și să trimită modificările în magazinul persistent. În cazul în care numărul de înregistrări este prea mare, iOS va pur și simplu salva din cauza lipsei de resurse. Chiar dacă un dispozitiv care rulează OS X poate avea resursele necesare pentru a executa cererea, acesta va fi lent și va consuma o mulțime de memorie.

O abordare alternativă este actualizarea înregistrărilor în loturi, însă acest lucru necesită prea mult timp și resurse. Pe iOS 7, este singura opțiune pe care dezvoltatorii iOS o au. Nu mai este cazul în iOS 8.

2. Soluția

Pe iOS 8 și OS X Yosemite, este posibil să vorbiți direct la magazinul persistent și să-i spuneți ce doriți să schimbați. Aceasta implică în general actualizarea unui atribut sau ștergerea unui număr de înregistrări. Apple a apelat această actualizare batch de caracteristici.

Există o serie de capcane pentru care trebuie să aveți grijă. Datele de bază au multe lucruri pentru dvs. și este posibil să nu le realizați până când nu utilizați actualizări batch. Validarea este un bun exemplu. Deoarece datele de bază efectuează actualizări batch direct pe magazinul persistent, cum ar fi o bază de date SQLite, Core Data nu poate efectua nici o validare a datelor introduse. Aceasta înseamnă că sunteți responsabil de a vă asigura că nu adăugați date nevalide la magazinul persistent.

Când ați folosi actualizările batch? Apple recomandă utilizarea acestei caracteristici numai dacă abordarea tradițională este prea resursă sau este intensivă. Dacă aveți nevoie să marcați sute sau mii de mesaje de e-mail ca citite, atunci actualizările batch sunt cea mai bună soluție pe iOS 8 și OS X Yosemite.

3. Cum funcționează?

Pentru a ilustra modul în care funcționează actualizările batch, vă sugerez să revedem Done, o aplicație Core Data simplu care gestionează o listă de sarcini. Vom adăuga un buton la bara de navigare care marchează fiecare articol din listă ca terminat.

Pasul 1: Configurarea Projet

Descărcați sau clonați proiectul de la GitHub și deschideți-l în Xcode 6. Rulați aplicația în Simulatorul iOS și adăugați câteva elemente de rezolvat.

Pasul 2: Crearea unui element Buton Bar

Deschis TSPViewController.m și să declare o proprietate, checkAllButton, de tip UIBarButtonItem în extensia de clasă privată din partea de sus.

@interface TSPViewController ()  @property (puternic, nonatomic) NSFetchedResultsController * fetchedResultsController; @property (puternic, nonatomic) UIBarButtonItem * checkAllButton; @property (puternic, nonatomic) NSIndexPath * selecție; @Sfârșit

Inițializați elementul buton bar din viewDidLoad metodă a TSPViewController clasa și setați-o ca elementul butonului stânga al elementului de navigare.

// Initialize Check All Button self.checkAllButton = [[UIBarButtonItem alloc] initWithTitle: @ Stilul "Check All": UIBarButtonItemStyleTargetul Bordered: auto action: @selector (checkAll :)]; // Configurează elementul de navigare self.navigationItem.leftBarButtonItem = self.checkAllButton;

Pasul 3: Implementați selectați toate: Metodă

selectați toate: metoda este destul de ușoară, dar există câteva avertismente pe care să le urmăriți. Uitați-vă la implementarea de mai jos.

- (void) checkAll: (id) expeditor // Crearea unei entități NSEntityDescription * entityDescription = [NSEntityDescription entityForName: @ "TSPItem" inManagedObjectContext: self.managedObjectContext]; // Initializeaza cererea de actualizare a lotului NSBatchUpdateRequest * batchUpdateRequest = [[NSBatchUpdateRequest alloc] initWithEntity: entityDescription]; // Configurați solicitarea de actualizare a lotului [batchUpdateRequest setResultType: NSUpdatedObjectIDsResultType]; [batchUpdateRequest setPropertiesToUpdate: @ @ "terminat": @YES]; // Execute Cererea lotului NSError * batchUpdateRequestError = nil; NSBatchUpdateResult * batchUpdateResult = (NSBatchUpdateResult *) [auto.managedObjectContext executeRequest: eroare batchUpdateRequest: & batchUpdateRequestError]; dacă (batchUpdateRequestError) NSLog (@ "Imposibil de executat cerere de actualizare batch."); NSLog (@ "% @,% @", batchUpdateRequestError, batchUpdateRequestError.localizedDescription);  altceva // Extract ID-uri de obiecte NSArray * objectIDs = batchUpdateResult.result; pentru (NSManagedObjectID * objectID în objectIDs) // Transformarea obiectelor gestionate în defecte NSManagedObject * managedObject = [auto.managedObjectContext objectWithID: objectID]; dacă (managedObject) [auto.managedObjectContext refreshObject: mergeObject mergeChanges: NO];  // Efectuați preluarea NSError * fetchError = nil; [auto.fetchedResultsController performFetch: & fetchError]; dacă (fetchError) NSLog (@ "Nu se poate efectua preluarea."); NSLog (@ "% @,% @", fetchError, fetchError.localizedDescription); 

Creați o cerere de lot

Începem prin crearea unui NSEntityDescription exemplu pentru TSPItem entitate și să o folosească pentru a inițializa o NSBatchUpdateRequest obiect.

// Crearea entității Descriere NSEntityDescription * entityDescription = [NSEntityDescription entityForName: @ "TSPItem" inManagedObjectContext: self.managedObjectContext]; // Initializeaza cererea de actualizare a lotului NSBatchUpdateRequest * batchUpdateRequest = [[NSBatchUpdateRequest alloc] initWithEntity: entityDescription];

Am setat tipul de rezultat al cererii de actualizare a lotului la NSUpdatedObjectIDsResultType, ceea ce înseamnă că rezultatul cererii de actualizare a lotului va fi o matrice care conține ID-urile obiectului, instanțe ale NSManagedObjectID clasă, a înregistrărilor modificate de cererea de actualizare a loturilor.

// Configurați solicitarea de actualizare a lotului [batchUpdateRequest setResultType: NSUpdatedObjectIDsResultType];

De asemenea, vom popula propertiesToUpdate proprietatea cererii de actualizare a lotului. Pentru acest exemplu, am stabilit propertiesToUpdate la un NSDictionary conținând o cheie, @"Terminat", cu valoare @DA. Acest lucru înseamnă pur și simplu că fiecare TSPItem înregistrarea va fi setată la Terminat, care este exact ceea ce ne dorim.

// Configurați solicitarea de actualizare a lotului [batchUpdateRequest setPropertiesToUpdate: @ @ "făcut": @YES];

Executați solicitarea de actualizare a lotului

Chiar dacă actualizările de pachete ocolește contextul obiect gestionat, executarea unei cereri de actualizare batch se face prin apelare executeRequest: eroare: pe o NSManagedObjectContext instanță. Primul argument este o instanță a NSPersistentStoreRequest clasă. Pentru a executa o actualizare batch, trecem cererea de actualizare a lotului pe care tocmai am creat-o. Acest lucru funcționează aripioare de la NSBatchUpdateRequest clasa este a NSPersistentStoreRequest subclasă. Al doilea argument este un pointer la un NSError obiect.

// Execute Cererea lotului NSError * batchUpdateRequestError = nil; NSBatchUpdateResult * batchUpdateResult = (NSBatchUpdateResult *) [auto.managedObjectContext executeRequest: eroare batchUpdateRequest: & batchUpdateRequestError];

Actualizarea contextului de obiect gestionat

Așa cum am menționat mai devreme, actualizările batchului ocolește contextul obiect gestionat. Acest lucru oferă actualizărilor batch puterea și viteza lor, dar înseamnă, de asemenea, că contextul obiect gestionat nu este conștient de schimbările pe care le-am făcut. Pentru a remedia această problemă, trebuie să facem două lucruri:

  • transforma obiectele gestionate care au fost actualizate prin actualizarea lotului în defecte
  • spuneți controlorului de rezultate preluate să efectueze o preluare pentru a actualiza interfața cu utilizatorul

Aceasta este ceea ce facem în următoarele câteva linii selectați toate: metodă. Mai întâi verificăm dacă cererea de actualizare a lotului a avut succes prin verificarea batchUpdateRequestError pentru zero. Dacă reușim, extragem matricea NSManagedObjectID instanțe din NSBatchUpdateResult obiect.

// Extrage ID-urile de obiecte NSArray * objectIDs = batchUpdateResult.result;

Apoi repetam peste objectIDs și solicitați contextul obiectului gestionat corespunzător NSManagedObject instanță. Dacă contextul obiect gestionat returnează un obiect gestionat valid, îl transformăm într-o greșeală invocând refreshObject: mergeChanges:, trecerea în obiectul gestionat ca primul argument. Pentru a forța obiectul gestionat într-o greșeală, trecem NU ca al doilea argument.

// Extrage ID-urile de obiecte NSArray * objectIDs = batchUpdateResult.result; pentru (NSManagedObjectID * objectID în objectIDs) // Transformarea obiectelor gestionate în defecte NSManagedObject * managedObject = [auto.managedObjectContext objectWithID: objectID]; dacă (managedObject) [auto.managedObjectContext refreshObject: mergeObject mergeChanges: NO]; 

Descărcarea înregistrărilor actualizate

Ultimul pas este de a spune controlorului de rezultate preluate să efectueze o preluare pentru a actualiza interfața cu utilizatorul. Dacă acest lucru nu reușește, vom înregistra eroarea corespunzătoare.

// Efectuați preluarea NSError * fetchError = nil; [auto.fetchedResultsController performFetch: & fetchError]; dacă (fetchError) NSLog (@ "Nu se poate efectua preluarea."); NSLog (@ "% @,% @", fetchError, fetchError.localizedDescription); 

În timp ce acest lucru poate părea complicat și destul de complex pentru o operație ușoară, rețineți că am ocol stivă de date de bază. Cu alte cuvinte, trebuie să avem grijă de unele gospodării care se fac de obicei de Core Data.

Pasul 4: Construiți și executați

Construiți proiectul și rulați aplicația în Simulatorul iOS sau pe un dispozitiv fizic. Faceți clic sau atingeți elementul de buton din bara din dreapta pentru a verifica fiecare element de rezolvare din listă.

Concluzie

Actualizările batch sunt o mare plus față de cadrul Core Data. Nu numai că răspunde nevoii pe care dezvoltatorii le-au avut de mulți ani, nu este dificil de implementat atâta timp cât păstrați câteva reguli de bază în minte. În tutorialul următor, vom examina mai atent preluarea asincronă, o altă caracteristică nouă a cadrului Core Data.

Cod