Sử dụng CoreData lưu trữ dữ liệu trên iOS
This post hasn't been updated for 3 years
Giới thiệu
Core Data bao gồm một số các đối tượng framework được tích hợp để cung cấp các chức năng lưu trữ dữ liệu. Core Data Stack được minh họa như hình dưới đây:
Như chúng ta có thể thấy trong hình trên, các ứng dụng iPhone nằm trên đỉnh của ngăn xếp và tương tác với các đối tượng dữ liệu được xử lý bởi managed object context. Ý nghĩa đặc biệt trong sơ đồ này là một thực tế rằng mặc dù các cấp thấp hơn trong ngăn xếp thực hiện một số lượng đáng kể các công việc liên quan trong việc cung cấp chức năng Core Data, các code ứng dụng không tương tác trực tiếp với chúng. Trước khi chuyển sang làm việc một cách thực tế hơn với Core Data, điều quan trọng là phải dành thời gian giải thích các yếu tố mà nó bao gồm trong stack Core Data với một chút chi tiết hơn.
Managed Objects
Managed objects là các đối tượng được tạo ra bởi code ứng dụng của bạn để lưu trữ dữ liệu. Một managed object có thể được coi như như là một hàng hoặc một bản ghi trong một bảng cơ sở dữ liệu quan hệ. Đối với mỗi bản ghi mới được thêm vào, một managed object mới phải được tạo ra để lưu trữ các dữ liệu. Tương tự như vậy, dữ liệu được lấy sẽ được trả lại trong form của managed object, mỗi bản ghi nó gắn với tiêu chuẩn thu hồi được xác định. Các managed object thực sự là những thể hiện của lớp NSManagedObject, hoặc một lớp con của chúng. Những đối tượng này được chứa và duy trì bởi bối managed object context.
Managed Object Context
Core Data dựa trên các ứng dụng không bao giờ tương tác trực tiếp với persistent store. Thay vào đó, các mã ứng dụng tương tác với các managed objects được chứa trong lớp managed object context của Core Data stack. Context duy trì trạng thái của các đối tượng liên quan đến nơi chứa dữ liệu bên dưới và quản lý các mối quan hệ giữa các managed object được xác định bởi mô hình managed object. Tất cả các tương tác với cơ sở dữ liệu bên dưới được tổ chức tạm thời trong context cho đến khi context được chỉ dẫn để lưu các thay đổi, tại điểm mà các thay đổi được truyền qua stack Core Data và được viết vào the persistent store.
Managed Object Model
Cho đến nay chúng tôi đã tập trung vào việc quản lý các đối tượng dữ liệu nhưng chưa nhìn vào các mô hình dữ liệu được định nghĩa như thế nào. Đây là nhiệm vụ của Managed Object Model trong đó xác định một khái niệm gọi là các thực thể. Một mô tả của lớp định nghĩa một kế hoạch chi tiết cho một thể hiện của đối tượng, các thực thể định nghĩa mô hình dữ liệu cho các managed object. Về bản chất, một thực thể tương tự như các lược đồ định nghĩa một bảng trong một cơ sở dữ liệu quan hệ. Như vậy, mỗi thực thể có một tập hợp các thuộc tính liên quan với nó mà nó định nghĩa dữ liệu được lưu trữ trong managed object có nguồn gốc từ (phát sinh từ) thực thể đó. Ví dụ, một thực thể Danh bạ có thể chứa các thuộc tính như tên, địa chỉ và số điện thoại. Ngoài các thuộc tính, các thực thể cũng có thể chứa các mối quan hệ, lấy (fetched) các thuộc tính và lấy các yêu cầu:
• Relationships - Trong context của Core Data, relationships như những mối quan hệ trong các hệ thống cơ sở dữ liệu quan hệ khác trong đó chúng đề cập đến cách một đối tượng dữ liệu liên kết đến đối tượng dữ liệu khác. Core Data relationships có thể là quan hệ một-một, một-nhiều nhiều-nhiều.
• Fetched property – Cái này cung cấp một thay thế cho việc xác định các mối quan hệ. Fetched properties cho phép thuộc tính của một đối tượng dữ liệu được truy cập từ một đối tượng dữ liệu khác như một mối quan hệ đã được xác định giữa các thực thể. Fetched properties thiếu tính linh hoạt trong các mối quan hệ và được miêu tả trong các tài liệu của Apple Core dữ liệu là "yếu, các mối quan hệ một chiều” phù hợp nhất với" các mối quan hệ lỏng lẻo ".
• Fetch request - Một truy vấn được xác định trước có thể được tham chiếu để lấy các đối tượng dữ liệu dựa trên vị ngữ được xác định. Ví dụ, một fetch request có thể được cấu hình thành một thực thể để lấy tất cả các đối tượng .
Bắt đầu với một demo cách dùng Core Data để tạo một Entity với một số attribute để lưu giữ thông tin trong app, sau đó là insert, update, delete dữ liệu.
Đầu tiên, tạo project, chú ý chọn check box “Use Core Data” như hình bên dưới
Sau khi tạo xong project, các bạn chọn file TamPN_CoreData.xcdatamodeld và thao tác như sau:
1: click vào button + (Add Entity)
2: type tên Entity vào, ở đây là “Device”
3: click vào dấu + để thêm attribute
Tiếp theo ta sẽ add các framework cần dùng cho app:
– Core Data
– FoundationFoundation
– UIKit
Tạo giao diện với storyboard
chúng ta sẽ có 1 file story board như sau:
Để làm được như trên, đầu tiên chọnView Controller rồi chọn Editor > Embed In > Navigation Controller.
Sau đó add Table View, Table View Cell, thêm button Add như hình bên dưới:
Tiếp theo kéo thêm một View Controller mới để tạo màn hình chi tiết của thiết bị. Thiết kế màn hình detail gồm 3 textfiefield: name, version, company, và 2 bar button: Cancel & Save.
Tạo các file View Controller
Vào File > New > New File, chọn “Cocoa Touch Class”:
Sau đó vào file storyboard, chọn View Controller của màn hình Detail, vào tab Class, chọn DetailDeviceVC
Tiếp theo kéo chuột phải các textfield, button với file DetailDeviceVC.h để tạo các biến, và hàm như bên dưới
Bây giờ sẽ lưu các thông tin người dùng nhập tại màn hình Detail rồi hiển thị dữ liệu ra màn hình thiết bị.
Lưu thông tin:
Khi khởi tạo dự án, chọn “Use Core Data”, nên XCode sẽ tự động tạo ra 1 “managed object context” trong Appdelegate. Chúng giúp thao tác với Database, nhưng chỉ trong Appdelegate thôi, nên mỗi khi muốn sử dụng, chúng ta phải khai báo nó dưới Appdelegate.
@implementation DetailDeviceVC
- (NSManagedObjectContext *)managedObjectContext {
NSManagedObjectContext *context = nil;
id delegate = [[UIApplication sharedApplication] delegate];
if ([delegate performSelector:@selector(managedObjectContext)]) {
context = [delegate managedObjectContext];
}
return context;
}
Tiếp theo, vẫn ở file “DetailDeviceVC.m” chúng ta sẽ viết hàm cho button “Cancel“: pop lại màn hình Device, hàm cho button “Save“: lưu thông tin và pop lại màn hình Device.
- (IBAction)cancelBtnTap:(id)sender {
[self.navigationController popViewControllerAnimated:YES];
}
- (IBAction)saveBtnTap:(id)sender {
NSManagedObjectContext *context = [self managedObjectContext];
// Create a new managed object
NSManagedObject *newDevice = [NSEntityDescription insertNewObjectForEntityForName:@"Device" inManagedObjectContext:context];
[newDevice setValue:self.name.text forKey:@"name"];
[newDevice setValue:self.version.text forKey:@"version"];
[newDevice setValue:self.company.text forKey: @"company"];
NSError *error = nil;
// Save the object to persistent store
if (![context save:&error]) {
NSLog(@"Can't Save! %@ %@", error, [error localizedDescription]);
}
[self.navigationController popViewControllerAnimated:YES];
}
Chú ý:để sử dụng thì chúng ta sẽ phải
import <CoreData/CoreData.h>
để thực hiện các thao tác khi dùng CoreData. Các bạn có thể “Run” app để test thử, tuy nhiên tại màn hình Device vẫn chưa hiện thị gì, bởi chúng ta chưa cho nó hiển thị dữ liệu vào table view. Bước tiếp theo sẽ lấy dữ liệu từ DataBase. Tại file “ViewController.m“, tương tự file “DetailDeviceVC.m”, khai báo một “managed object context”
@implementation DetailDeviceVC
- (NSManagedObjectContext *)managedObjectContext {
NSManagedObjectContext *context = nil;
id delegate = [[UIApplication sharedApplication] delegate];
if ([delegate performSelector:@selector(managedObjectContext)]) {
context = [delegate managedObjectContext];
}
return context;
}
Chúng ta dùng một biến “devices” để lưu danh sách thiết bị đã lưu trong database:
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
// Fetch the devices from persistent data store
NSManagedObjectContext *managedObjectContext = [self managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:@"Device"];
self.devices = [[managedObjectContext executeFetchRequest:fetchRequest error:nil] mutableCopy];
[self.tableView reloadData];
}
Hiển thị dữ liệu trên TableView Tiếp theo, chúng ta sẽ đẩy dữ liệu ở biến devices vào TableView:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return self.devices?[self.devices count]:0;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// static NSString *CellIdentifier = @"Cell";
// UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
static NSString *const Cell = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:Cell];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:Cell];
}
// Configure the cell...
NSManagedObject *device = [self.devices objectAtIndex:indexPath.row];
[cell.textLabel setText:[NSString stringWithFormat:@"%@ %@", [device valueForKey:@"name"], [device valueForKey:@"version"]]];
[cell.detailTextLabel setText:[device valueForKey:@"company"]];
return cell;
}
** Delete**
Phần delete dữ liệu xử lý rất dễ dàng, chúng ta chỉ cần delete 1 row bình thường như trong table view và thêm vào xử lý dữ liệu Core Data như sau:
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
// Return NO if you do not want the specified item to be editable.
return YES;
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
NSManagedObjectContext *context = [self managedObjectContext];
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Delete object from database
[context deleteObject:[self.devices objectAtIndex:indexPath.row]];
NSError *error = nil;
if (![context save:&error]) {
NSLog(@"Can't Delete! %@ %@", error, [error localizedDescription]);
return;
}
// Remove device from table view
[self.devices removeObjectAtIndex:indexPath.row];
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
}
** Update** Trong phần update dữ liệu, chúng ta phải tạo 1 segue: khi chọn 1 row sẽ push sang màn hình detai:
tiếp theo cần đặt tên cho segue này là “updateSegue“, sau đó, tại file ViewController.m, và thêm hàm “prepairForSegue” để thực hiện thao tác push:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:@"updateSegue"]) {
DetailDeviceVC *destViewController = segue.destinationViewController;
}
}
Chú ý: import “DetailDeviceVC.h".
Lúc này, khi chọn 1 row ở bảng device, chúng ta đã chuyển sang màn hình DetailDevice, nhưng chúng ta chưa có dữ liệu được chọn. Chính vì vậy, ở file DetailDeviceVC.h chúng ta sẽ thêm 1 trường để lưu thông tin device đã chọn:
@property (strong) NSManagedObject *device;
truyền thông tin device đã chọn khi push màn hình:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:@"updateSegue"]) {
NSManagedObject *selectedDevice = [self.devices objectAtIndex:[[self.tableView indexPathForSelectedRow] row]];
DetailDeviceVC *destViewController = segue.destinationViewController;
destViewController.device = selectedDevice;
}
}
Để hiện thị thông tin của device đã chọn, chúng ta thao tác trong “viewDidLoad”
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
if (self.device) {
[self.name setText:[self.device valueForKey:@"name"]];
[self.version setText:[self.device valueForKey:@"version"]];
[self.company setText:[self.device valueForKey:@"company"]];
}
}
Bước tiếp theo, chúng ta sẽ lưu thông tin sau khi edit vào database:
Ở hàm “save“, chúng ta sẽ có 2 trường hợp:
– Lưu thông tin vừa mới edit, tức update
– Lưu thông tin mới hay là thêm mới 1 device.
Phần thêm mới chúng ta đã xử lý ở bên trên, nên chúng ta sẽ thêm phần update vào hàm “save” như sau:
- (IBAction)saveBtnTap:(id)sender {
NSManagedObjectContext *context = [self managedObjectContext];
if (self.device) {
// Update existing device
[self.device setValue:self.name.text forKey:@"name"];
[self.device setValue:self.version.text forKey:@"version"];
[self.device setValue:self.company.text forKey:@"company"];
}else{
// Create a new managed object
NSManagedObject *newDevice = [NSEntityDescription insertNewObjectForEntityForName:@"Device" inManagedObjectContext:context];
[newDevice setValue:self.name.text forKey:@"name"];
[newDevice setValue:self.version.text forKey:@"version"];
[newDevice setValue:self.company.text forKey: @"company"];
}
NSError *error = nil;
// Save the object to persistent store
if (![context save:&error]) {
NSLog(@"Can't Save! %@ %@", error, [error localizedDescription]);
}
[self.navigationController popViewControllerAnimated:YES];
}
Test app
Và bây giờ chúng ta có thể “Run” để test chương trình
1- Add / Update / Detail
2- Show
Tổng kết:
Core Data framework stack cung cấp một thay thế linh hoạt để trực tiếp quản lý dữ liệu bằng cách sử dụng SQLite hoặc các cơ chế lưu trữ dữ liệu khác. Bằng cách cung cấp một lớp đối tượng trừu tượng được được định hướng trên các dữ liệu của việc quản lý lưu trữ dữ liệu được thực hiện dễ dàng hơn cho các nhà phát triển ứng dụng iPhone.
All Rights Reserved