Table of Contents
Introducere: De ce datele de bază și NSFetchedResultController Matter
În dezvoltarea iOS modernă, furnizarea unei interfețe de utilizator fluide, receptive depinde adesea de cât de eficient se ocupă aplicația dumneavoastră de date care se schimbă în timp. Fie că construiți un flux social, un manager de sarcini sau un sistem de inventariere, datele afișate utilizatorilor sunt rareori statice. Core Data
Acest articol oferă un ghid practic și aprofundat pentru utilizarea datelor de bază cu notificări este cheia. Când obiectele sunt introduse, actualizate sau șterse, contextul transmite aceste modificări, care este exact ceea ce pârghii pentru a menține UI-ul consecvent.
Pentru documentația oficială a dezvoltatorului, a se vedea Cadrul de date de bază al Apple.
NSFetchedResultsController: Puntea dintre datele de bază și UI
este un obiect de control conceput pentru a gestiona eficient rezultatele returnate dintr-o cerere de colectare a datelor de bază, în special atunci când datele subiacente se așteaptă să se modifice. Monitorizează contextul obiectului gestionat relevant și raportează automat modificările prin protocolul delegat.
Caracteristici cheie
- Urmărire automată a schimbării: FRC ascultă notificările contextului și le traduce în apelurile de delegat structurate [, , .
- Built-in secţionare: Prin specificarea unui [, grupurile de controlori aduc rezultate în secţiuni, făcând ca aceasta să fie trivială pentru a afişa vederi de masă secţionate sau pentru a vedea colecţia.
- Optimizarea performanţei: FRC utilizează defecte şi caching sub capotă. Aduce date doar după cum este necesar şi poate folosi opţional un cache persistent pentru a evita re-fettting atunci când contextul obiectului gestionat este salvat.
Metode delegate în detaliu
Pentru a beneficia pe deplin de controlor, trebuie să implementați . Cel mai frecvent model este de a utiliza aceste apeluri înapoi în interiorul unui sau delegat pentru a actualiza lotul UI. De exemplu:
func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
tableView.beginUpdates()
}
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>,
didChange anObject: Any,
at indexPath: IndexPath?,
for type: NSFetchedResultsChangeType,
newIndexPath: IndexPath?) {
switch type {
case .insert:
tableView.insertRows(at: [newIndexPath!], with: .fade)
case .delete:
tableView.deleteRows(at: [indexPath!], with: .fade)
case .update:
tableView.reloadRows(at: [indexPath!], with: .fade)
case .move:
tableView.moveRow(at: indexPath!, to: newIndexPath!)
@unknown default:
tableView.reloadData()
}
}
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
tableView.endUpdates()
}
Acest model asigură că vizualizarea de pe tabel animează modificările în sincronizare cu datele de bază, prevenind pâlpâirea sau inconsecvența.
Punerea în aplicare pas cu pas
Mai jos este un exemplu complet, gata de producție folosind Swift 5, care vizează iOS 15+. Vom presupune o entitate simplă numită ] cu atribute (Sfârșit) și (Bool) și (Date).
1. Setați stiva de date de bază
În aplicaţiile sau în aplicaţiile dumneavoastră (frecvente în aplicaţiile SwiftUI), creaţi recipientul persistent:
class PersistenceController {
static let shared = PersistenceController()
let container: NSPersistentContainer
init() {
container = NSPersistentContainer(name: "YourModelName")
container.loadPersistentStores { storeDescription, error in
if let error = error as NSError? {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
}
container.viewContext.automaticallyMergesChangesFromParent = true
}
}
2. Creați subclasa NSManagedObject
Utilizați editorul de modele de date Xcode pentru a genera fișierele clasei sau pentru a le crea manual. Asigurați-vă că entitatea dumneavoastră utilizează setarea corectă a modulului de clasă.
3. Configurați cererea de aport și FRC
În controlerul vizual, configurați cererea de preluare și inițializați controlerul de rezultate obținute. Este cel mai bine să efectuați acest lucru în sau init un model de vizualizare.
lazy var fetchedResultsController: NSFetchedResultsController<Task> = {
let fetchRequest: NSFetchRequest<Task> = Task.fetchRequest()
let sortDescriptor = NSSortDescriptor(key: "dueDate", ascending: true)
fetchRequest.sortDescriptors = [sortDescriptor]
// Optional: limit results with batch size for large datasets
fetchRequest.fetchBatchSize = 20
let controller = NSFetchedResultsController(
fetchRequest: fetchRequest,
managedObjectContext: PersistenceController.shared.container.viewContext,
sectionNameKeyPath: "completionStatus", // e.g., a transient attribute or a computed property
cacheName: nil
)
controller.delegate = self
try? controller.performFetch()
return controller
}()
4. Conduceți vizualizarea mesei cu FRC
Metodele tale devin banale:
func numberOfSections(in tableView: UITableView) -> Int {
fetchedResultsController.sections?.count ?? 0
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let sectionInfo = fetchedResultsController.sections![section]
return sectionInfo.numberOfObjects
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "TaskCell", for: indexPath)
let task = fetchedResultsController.object(at: indexPath)
configure(cell, with: task)
return cell
}
Observați că nu apelăm manual
Utilizarea avansată şi cele mai bune practici
Siguranţa firului
Contextele de bază ale datelor nu sunt sigure. Utilizați întotdeauna (care rulează pe coada principală) pentru toate cererile de preluare a banilor legate de UI și FRC-uri. Pentru munca de fundal, creați un context de coadă privată și fuzionați modificările contextului vizual. Evitați utilizarea acelorași FRC pe cozi multiple.
Caching
Parametrul poate îmbunătăți performanța de lansare prin informații de secțiune și obiect persistente. Totuși, dacă cererea dumneavoastră de preluare sau modelul de date se modifică, trebuie să ștergeți cache-ul () pentru a evita corupția.
Manipularea seturilor mari de date
Utilizaţi pentru a limita numărul de obiecte introduse în memorie. De asemenea, luaţi în considerare setarea ] numai dacă aveţi nevoie de acces imediat la fiecare proprietate. Pentru datele mari de relaţie, prefetting cu poate preveni defecte repetate.
Integrarea cu SwiftUI
În timp ce este UIKit-centric, puteți să-l utilizați în SwiftUI prin ambalaj într-un sau folosind mai nou ambalaj de proprietate (care utilizează în interior un mecanism similar). Pentru date dinamice complexe în SwiftUI, este de obicei suficient, dar FRC vă oferă un control fin-grained asupra animațiilor și a lotingului.
Capturi comune şi cum să le evităm
- Uitarea : Controlorul nu va executa pachetul până când nu apelați această metodă. Fă-o o dată, de obicei imediat după inițializare.
- Nu setează delegatul: Fără un delegat, modificările nu se vor propaga în vederea tabelului.
- Descriptori de tip incorect:[ Dacă numele secțiunii dvs. de cale cheie nu se potrivește cu primul descriptor de tip, secțiunile pot apărea din ordine. Asigurați-vă că atributul utilizat în apare în descriptori de tip.
- Folosind alături de FRC: Apelare în timp ce FRC este animarea schimbărilor poate provoca accidente.Lasă metodele delegate să se ocupe de toate actualizările UI.
- Ignorarea contextului de date de bază salvează eșecurile:[ Dacă salvați contextul și apare o eroare, FRC nu ar putea fi notificat. Întotdeauna se manipulează erorile și se ia în considerare utilizarea în contexte de fond.
Considerații privind performanța
este deja eficient, dar aici sunt optimizari suplimentare:
- Folosiţi predicate cu înţelepciune:Un predicat complex poate încetini aducerea iniţială.Utilizaţi atribute indexate acolo unde este posibil.
- Limit proprietăți aduse: Dacă aveți nevoie doar de anumite atribute, setați ] la cererea de preluare.
- Actualizări ale bazei:[ Când se fac multe schimbări, le înfăşuraţi într-un bloc pentru a reduce numărul de apeluri de delegat.
- Dacă știi că vei accesa toate obiectele într-un set de rezultate, folosește pentru a le pre-aduce, dar fii precaut cu memoria.
Pentru o scufundare mai profundă în performanța datelor de bază, a se vedea Ghidul de performanță al datelor de bază al Apple.
Exemple reale
Exemplul 1: Aplicație de chat
O aplicație de mesagerie afișează o listă de conversații sortate de cel mai recent mesaj. Mesajele noi primite ar trebui să apară instantaneu. Folosind un FRC, vizualizarea contactului poate fi abonată la modificări doar pentru entitățile de conversație ale utilizatorului curent. Secțiune după data grupurilor de mesaje în "Astăzi," "Ieri" etc.
Exemplul 2: Managementul inventarului
O aplicație de comerț electronic arată produse în categorii. Când nivelurile stocurilor se schimbă de la o sincronizare de fundal, FRC actualizează automat UI. Prin setarea ] la 50, vizualizarea rămâne receptivă chiar și cu mii de elemente.
Exemplul 3: Lista de făcut cu categoriile
Exemplul clasic: sarcinile grupate în "Supradue," "Astăzi" și "Upcoming." pot fi un atribut derivat tranzitoriu calculat din și data curentă. Asigurați-vă că aceeași valoare derivată este utilizată în descriptorul de tip pentru a evita neconcordanțele.
Concluzie
Masterarea combinației de date de bază și este o piatră de temelie a construcției de aplicații iOS robuste și dinamice. Descarcând ridicarea grea a urmăririi schimbării și sincronizarea UI la cadrele Apple, vă puteți concentra pe crearea unei experiențe de utilizator excelente, în loc să scrieți codul de gestionare a datelor cu cazane.
Fie că sunteți păstrarea unei aplicații UIKit moștenire sau adoptarea SwiftUI cu , principiile rămân aceleași: înțelege graficul dvs. obiect, configurați cu atenție cererile de a aduce cereri, și lăsați controlorul de rezultate obținute să facă ceea ce face cel mai bine. Cu practicile prezentate în acest articol
Pentru explorarea ulterioară, verificați Tutorialul de date de bază al lui Ray Wenderlich și articolul NShipster despre NSFetchedResultsController.