Sunday, September 9, 2012

Hướng dẫn lập trình Coredata trên iphone

http://congdongios.com/showthread.php?46-H%C6%B0%E1%BB%9Bng-d%E1%BA%ABn-l%E1%BA%ADp-tr%C3%ACnh-Coredata-tr%C3%AAn-iphone

Trong tất cả những cách để lưu trữ dữ liệu trên iPhone, Core data là một trong những tốt nhất để sử dụng cho việc lưu trữ dữ liệu tương đối lớn Nó có thể giảm chi phí bộ nhớ của ứng dụng của bạn, tăng năng đáp ứng, và giúp bạn tiết kiệm từ viết rất nhiều mã boilerplate.

Tuy nhiên, để bạn có thể học được cách viết core data đòi hỏi công sức bỏ ra rất lớn. Đó là nơi mà loạt bài hướng dẫn này có mục tiêu là để giúp bạn để tăng tốc độ với những điều cơ bản của core data một cách nhanh chóng.

Trong một phần của loạt bài này, chúng ta sẽ tạo ra một mô hình dữ liệu hình ảnh cho các đối tượng của chúng tôi, chạy một thử nghiệm nhanh chóng để đảm bảo nó hoạt động, và sau đó đưa lên UItableview để chúng ta có thể thấy một danh sách của chúng tôi các đối tượng.

Trong phần thứ hai của loạt bài, chúng ta sẽ thảo luận làm thế nào để import hoặc preload các dữ liệu hiện có trước khi tải vào core data để load một số dữ liệu mặc định khi ứng dụng của chúng tôi bắt đầu lên.

Trong phần cuối cùng của loạt bài, chúng ta sẽ thảo luận làm thế nào chúng ta có thể tối ưu hóa ứng dụng của chúng tôi bằng cách sử dụng NSFetchedResultsController, để giảm chi phí bộ nhớ và cải thiện thời gian đáp ứng của ứng dụng.

Trước khi tiến hành hướng dẫn này, tôi khuyên bạn nên tìm hiểu trước về SQLite và cách sử dụng nó trên iphone, vì đơn giản là trên iphone thì chủ yếu sử dụng SQLite để lưu trữ dữ liệu


1) Creating a Core Data Project

Hãy bắt đầu! Tạo một ứng dụng mới Window-based Application, và chọn "Use Core Data for storage", và đặt tên cho dự án "FailedBanksCD."
Trước khi chúng tôi bắt đầu, chúng ta hãy xem nhanh các dự án. Đầu tiên mở rộng và nhấp đúp chuột vào FailedBanksCD.xcdatamodel. Bạn sẽ thấy một trình soạn thảo trực quan sẽ bật lên - đây là những gì chúng tôi sẽ được sử dụng trong một phút sơ đồ đối tượng mô hình của chúng tôi. Đi trước và đóng nó cho bây giờ.
Sau đó có một cái nhìn tại FailedBanksCDAppDelegate.m. Bạn sẽ thấy rằng có một số chức năng mới ở đây được thực hiện cho chúng ta, để thiết lập dữ liệu Core "chồng". Một tạo ra một bối cảnh đối tượng được quản lý, người ta tạo ra một mô hình đối tượng được quản lý, và tạo ra một điều phối viên cửa hàng liên tục. Huh?
Đừng lo lắng. Tên âm thanh khó hiểu lúc đầu tiên, nhưng một khi bạn có được một "mental shortcut" cho những gì họ đang tất cả về họ là dễ hiểu.
Managed Object Model: Bạn có thể nghĩ về điều này như là lược đồ cơ sở dữ liệu. Đó là một lớp có chứa các định nghĩa cho mỗi các đối tượng (còn gọi là "Các đối tượng") mà bạn đang lưu trữ trong cơ sở dữ liệu. Thông thường, bạn sẽ sử dụng trình biên tập hình ảnh bạn chỉ nhòm để thiết lập những gì các đối tượng trong cơ sở dữ liệu, những gì thuộc tính của họ, và làm thế nào chúng liên quan đến nhau. Tuy nhiên, bạn có thể làm điều này với mã quá!

Persistent Store Coordinator: Bạn có thể nghĩ về điều này như kết nối cơ sở dữ liệu. Đây là nơi bạn thiết lập tên và địa điểm thực tế của những cơ sở dữ liệu sẽ được sử dụng để lưu trữ các đối tượng, và bất cứ lúc nào một bối cảnh đối tượng được quản lý nhu cầu để tiết kiệm một cái gì đó nó đi qua điều phối viên duy nhất.
Managed Object Context Bạn có thể nghĩ về điều này như là một "pad đầu" cho các đối tượng từ cơ sở dữ liệu. Nó cũng quan trọng nhất của ba đối với chúng tôi, bởi vì chúng ta sẽ làm việc này nhất. Về cơ bản, bất cứ khi nào bạn cần phải được các đối tượng đối tượng chèn, hoặc xóa các đối tượng, bạn gọi phương pháp trên bối cảnh đối tượng quản lý (hoặc ít nhất là hầu hết thời gian!)
Đừng lo lắng quá nhiều về những phương pháp này - bạn sẽ không phải để gây rối với họ nhiều. Tuy nhiên, rất tốt để biết rằng họ đang có và những gì họ đại diện.
Defining Our Model
Khi chúng tôi tạo ra các bảng cơ sở dữ liệu của chúng tôi trong hướng dẫn SQLite, chúng tôi đã có một bảng duy nhất chứa tất cả các dữ liệu cho một ngân hàng thất bại. Để làm giảm lượng dữ liệu trong bộ nhớ cùng một lúc (cho mục đích học tập), chúng ta chỉ cần kéo các tập hợp con của các lĩnh vực mà chúng ta cần để hiển thị trong xem bảng đầu tiên của chúng tôi.
Vì vậy, chúng ta có thể bị cám dỗ để thiết lập mô hình của chúng tôi theo cùng một cách với dữ liệu cốt lõi. Tuy nhiên, với các dữ liệu cốt lõi, bạn không thể lấy chỉ một số thuộc tính của một đối tượng - bạn có để lấy toàn bộ đối tượng. Tuy nhiên, nếu chúng ta yếu tố các đối tượng thành hai phần - phần FailedBankInfo và phần FailedBankDetails - chúng ta có thể thực hiện cùng một điều chính xác.
Vì vậy, chúng ta hãy xem làm thế nào điều này sẽ làm việc. Mở soạn thảo mô hình trực quan (mở rộng Tài nguyên và FailedBanksCD.xcodedatamodel nhấp đúp chuột).
Hãy bắt đầu bằng cách tạo ra một đối tượng trong mô hình của chúng tôi gọi là "thực thể" trong dữ liệu cốt lõi nói. Trong khung bên trái, nhấn nút dấu cộng để thêm một thực thể mới như sau:



Sau khi cách nhấn vào dấu cộng, nó sẽ tạo ra một thực thể mới, và hiển thị các thuộc tính cho các thực thể trong bảng điều khiển bên phải như sau:



Tên FailedBankInfo Entity. Lưu ý rằng nó liệt kê các lớp học như là một lớp con của NSManagedObject. Đây là lớp mặc định cho các thực thể, mà chúng ta sẽ sử dụng cho bây giờ - sau đó chúng tôi sẽ trở lại và thiết lập các đối tượng tùy chỉnh.
Vì vậy, hãy thêm một số thuộc tính. Trước tiên, hãy chắc chắn rằng thực thể của bạn được chọn bằng cách click vào tên thực thể trong bảng điều khiển bên trái, hoặc sơ đồ cho các thực thể trong chế độ xem sơ đồ. Trong bảng điều khiển trung, nhấp vào nút dấu cộng và sau đó nhấp vào "Add thuộc tính" như sau:





Trong cửa sổ tài sản trên bên phải, đặt tên thuộc tính "tên" và thiết lập Type "String" như sau:





Bây giờ, lặp lại điều này để thêm hai thuộc tính, "thành phố" và "nhà nước", cả hai dây.
Tiếp theo, chúng ta cần phải tạo ra một thực thể cho FailedBankDetails. Tạo ra một thực thể giống như cách bạn đã làm trước đây, và thêm các thuộc tính sau: zip của 32 loại Int, closeDate của ngày loại, và updatedDate ngày loại.
Cuối cùng, chúng ta cần phải kết hợp hai loại này. Chọn FailedBankInfo, và nhấn nút dấu cộng trong phần panel ở giữa, nhưng lần này chọn "Add relationship":




Đặt tên cho mối quan hệ "chi tiết", và thiết lập các điểm đến như là "FailedBankDetails".
Ok, vì vậy chúng tôi đã làm những gì làm ở đây? Chúng tôi chỉ cần thiết lập một mối quan hệ trong dữ liệu cốt lõi, mà liên kết một thực thể cho đơn vị khác. Trong trường hợp này, chúng tôi đang thiết lập một mối quan hệ một-to-- mỗi FailedBankInfo sẽ có đúng 1 FailedBankDetails. Đằng sau hậu trường, dữ liệu cốt lõi sẽ thiết lập cơ sở dữ liệu của chúng tôi để bảng FailedBankInfo của chúng tôi có một trường ID của đối tượng tương ứng FailedBankDetails.
Apple khuyến cáo rằng bất cứ khi nào bạn tạo ra một liên kết từ một đối tượng khác, bạn tạo một liên kết từ các đối tượng khác sẽ trở lại là tốt. Vì vậy, hãy làm này.
Bây giờ thêm một mối quan hệ để "FailedBankDetails" có tên là "thông tin", thiết lập các điểm đến "FailedBankInfo", và thiết lập các nghịch đảo để "chi tiết".
Ngoài ra, thiết lập các quy tắc xóa cho cả hai mối quan hệ "xếp tầng". Điều này có nghĩa rằng nếu bạn xóa một đối tượng với dữ liệu cốt lõi, dữ liệu cốt lõi sẽ xóa các đối tượng liên quan. Điều này làm cho cảm giác trong trường hợp này bởi vì một FailedBankDetails không có nghĩa là bất cứ điều gì mà không có một FailedBankInfo tương ứng.

Testing our Model


Tin tưởng rằng nó hay không, có lẽ điều quan trọng nhất mà chúng ta cần phải làm. Bây giờ nó chỉ là một vấn đề thử nghiệm bằng cách sử dụng dữ liệu Core và đảm bảo nó hoạt động!
Trước tiên, hãy kiểm tra ra thêm một đối tượng kiểm tra cơ sở dữ liệu của chúng tôi. Mở FailedBanksCDAppDelegate.m và thêm dòng sau vào đầu applicationDidFinishLaunching:


NSManagedObjectContext *context = [self managedObjectContext];
NSManagedObject *failedBankInfo = [NSEntityDescription
insertNewObjectForEntityForName:@"FailedBankInfo"
inManagedObjectContext:context];
[failedBankInfo setValue:@"Test Bank" forKey:@"name"];
[failedBankInfo setValue:@"Testville" forKey:@"city"];
[failedBankInfo setValue:@"Testland" forKey:@"state"];
NSManagedObject *failedBankDetails = [NSEntityDescription
insertNewObjectForEntityForName:@"FailedBankDetail s"
inManagedObjectContext:context];
[failedBankDetails setValue:[NSDate date] forKey:@"closeDate"];
[failedBankDetails setValue:[NSDate date] forKey:@"updatedDate"];
[failedBankDetails setValue:[NSNumber numberWithInt:12345] forKey:@"zip"];
[failedBankDetails setValue:failedBankInfo forKey:@"info"];
[failedBankInfo setValue:failedBankDetails forKey:@"details"];
NSError *error;
if (![context save:&error]) {
NSLog(@"Whoops, couldn't save: %@", [error localizedDescription]);
}
Trong dòng đầu tiên, chúng ta lấy một con trỏ đến bối cảnh đối tượng quản lý của chúng tôi bằng cách sử dụng chức năng trợ giúp mà đi kèm với mẫu.
Sau đó, chúng tôi tạo ra một thể hiện mới của một NSManagedObject cho thực thể FailedBankInfo của chúng ta, bằng cách gọi insertNewObjectForEntityForName. Mỗi đối tượng cốt lõi cửa hàng dữ liệu xuất phát từ NSManagedObject. Một khi bạn có một thể hiện của đối tượng, bạn có thể gọi setValue cho bất kỳ thuộc tính mà bạn định nghĩa trong trình soạn thảo trực quan để thiết lập lên các đối tượng.
Vì vậy, chúng tôi đi trước và thiết lập một ngân hàng kiểm tra, cho cả hai FailedBankInfo và FailedBankDetails. Tại thời điểm này các đối tượng chỉ cần sửa đổi trong meomry - để lưu trữ chúng trở lại cơ sở dữ liệu chúng tôi cần phải gọi tiết kiệm trên các managedObjectContext.
Đó là tất cả để có nó để chèn các đối tượng - không có mã SQL cần thiết!
Trước khi chúng tôi cố gắng này ra, chúng ta hãy thêm một số mã trong đó ra khỏi danh sách tất cả các đối tượng trong cơ sở dữ liệu:

NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
entityForName:@"FailedBankInfo" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
for (NSManagedObject *info in fetchedObjects) {
NSLog(@"Name: %@", [info valueForKey:@"name"]);
NSManagedObject *details = [info valueForKey:@"details"];
NSLog(@"Zip: %@", [details valueForKey:@"zip"]);
}
[fetchRequest release];

Ở đây chúng tôi tạo ra một đối tượng mới được gọi là một yêu cầu tìm nạp. Bạn có thể nghĩ rằng một yêu cầu tải như là một mệnh đề SELECT. Chúng tôi gọi entityForName để có được một con trỏ đến các thực thể FailedBankInfo chúng tôi muốn lấy, và sau đó sử dụng setEntity để nói lấy yêu cầu của chúng tôi đó là loại thực thể chúng ta muốn.
Chúng tôi sau đó gọi executeFetchRequest trên bối cảnh đối tượng được quản lý để kéo tất cả các đối tượng trong bảng FailedBankInfo vào "pad đầu" của chúng tôi. Chúng tôi sau đó lặp qua mỗi NSManagedObject, và sử dụng valueForKey để kéo ra khỏi các phần khác nhau.
Lưu ý rằng mặc dù chúng ta đã rút ra khỏi các đối tượng từ bảng FailedBankInfo, chúng tôi vẫn có thể truy cập vào liên kết FailedBankDetails đối tượng bằng cách acessing tài sản chi tiết trên thực thể FAiledBankInfo.
Làm thế nào để công việc này? Phía sau hậu trường, khi bạn truy cập dữ liệu cốt lõi rằng tài sản thông báo rằng nó không có các dữ liệu trong bối cảnh, và "lỗi", mà cơ bản có nghĩa là nó chạy qua cơ sở dữ liệu và kéo theo đó dữ liệu cho bạn ngay khi bạn cần nó . Khá thuận lợi!
Mã này chạy có một cái nhìn trong cửa sổ đầu ra của bạn, và bạn sẽ thấy một ngân hàng kiểm tra trong cơ sở dữ liệu của bạn mỗi khi bạn chạy chương trình.




Seeing the Raw SQL Statements


Tôi không biết về bạn, nhưng khi tôi đang làm việc trên loại công cụ này, tôi thực sự muốn thấy các câu lệnh SQL thực tế để hiểu những điều đang làm việc (và chắc chắn nó làm những gì tôi mong đợi!)
Một lần nữa, Apple đã cung cấp một giải pháp dễ dàng này. Mở các chương trình chạy thả xuống trong XCode và thực thi FailedBanksCD của bạn. Nhấp chuột phải vào đó và bấm vào "Get Info" Điều hướng đến tab Arguments và thêm đối số sau đây: "-com.apple.CoreData.SQLDebug 1". Khi bạn đang thực hiện, nó sẽ giống như sau:




Bây giờ khi bạn chạy mã của bạn, trong đầu ra gỡ lỗi, bạn sẽ thấy dấu vết báo cáo hữu ích như này hiển thị cho bạn những gì đang xảy ra:


SELECT Z_VERSION, Z_UUID, Z_PLIST FROM Z_METADATA
SELECT Z_MAX FROM Z_PRIMARYKEY WHERE Z_ENT = ?
UPDATE Z_PRIMARYKEY SET Z_MAX = ? WHERE Z_ENT = ? AND Z_MAX = ?
INSERT INTO ZFAILEDBANKDETAILS(Z_PK, Z_ENT, Z_OPT, ZINFO,
ZUPDATEDDATE, ZZIP, ZCLOSEDATE) VALUES(?, ?, ?, ?, ?, ?, ?)
INSERT INTO ZFAILEDBANKINFO(Z_PK, Z_ENT, Z_OPT, ZDETAILS, ZNAME,
ZSTATE, ZCITY) VALUES(?, ?, ?, ?, ?, ?, ?)
SELECT 0, t0.Z_PK, t0.Z_OPT, t0.ZNAME, t0.ZSTATE, t0.ZCITY, t0.ZDETAILS
FROM ZFAILEDBANKINFO t0
SELECT 0, t0.Z_PK, t0.Z_OPT, t0.ZUPDATEDDATE, t0.ZZIP, t0.ZCLOSEDATE,
t0.ZINFO FROM ZFAILEDBANKDETAILS t0 WHERE t0.Z_PK = ?


Vì vậy, ở đây chúng ta thấy mọi thứ đang làm việc như chúng ta mong đợi. Hai lựa chọn đầu tiên và cập nhật dữ liệu cốt lõi làm một số công việc sổ sách kế toán theo dõi những gì các ID tiếp theo cho các thực thể nên được.
Sau đó, chúng tôi đã chèn vào các chi tiết và bảng thông tin. Sau đó, chúng tôi lựa chọn ngân hàng thông tin toàn bộ bảng trong truy vấn của chúng tôi. Sau đó, như chúng ta lặp qua các kết quả, mỗi lần chúng ta truy cập các biến chi tiết, đằng sau các dữ liệu Core cảnh lỗi và các vấn đề khác tuyên bố chọn để có được những dữ liệu từ bảng ZFAILEDBANKDETAILS.



Auto Generating Model Files

Cho đến nay, chúng tôi đã được sử dụng NSManagedObject để làm việc với các thực thể của chúng tôi. Đây không phải là cách tốt nhất để làm những việc, bởi vì nó khá dễ dàng để làm cho một sai lầm và gõ một cái tên thuộc tính không chính xác, hoặc tập hợp dữ liệu bằng cách sử dụng các loại sai, vv
Cách tốt hơn để làm những việc là để tạo ra một tập tin mẫu cho mỗi thực thể. Bạn có thể làm điều này bằng tay, nhưng XCode làm cho điều này khá dễ dàng với một máy phát điện lớp.
Chúng ta hãy thử nó ra. Mở FailedBanksCD.xcdatamodel, trên thực thể FailedBankInfo, và hãy vào File \ New File. Chọn "Class Touch ca cao", và bạn sẽ thấy một mẫu mới cho "Class đối tượng quản lý." Lựa chọn này và nhấn Next, và sau đó kích Next một lần nữa trên quan điểm sau đây.

No comments:

Post a Comment