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.

Thursday, September 6, 2012

Làm việc với cơ sở dữ liệu trong iphone bằng cách sử dụng Core Data


The Core Data Stack

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:


Core Data Stack Architecture diagram



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 tương tác với các đối tượng dữ liệu được xử bởi managed object context. Ý nghĩa đặc biệt trong sơ đồ này là một thực tế rằng mặc 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 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ư 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ự 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 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 quản 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 đượ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ể 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 nguồn gốc từ (phát sinh từ) thực thể đó. Ví dụ, một thực thể Danh bạ thể chứa các thuộc tính như tên, địa chỉ 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 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 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 "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 liên lạc tên trường gắn với "John Smith".

Persistent Store Coordinator

Persistent Store Coordinator có trách nhiệm phối hợp truy cập vào các nơi chứa nhiều đối tượng liên tục (persistent). Như một nhà phát triển iPhone, bạn sẽ không bao giờ tương tác trực tiếp với điều phối viên của nơi chứa (store), trên thực tế, rất hiếm khi sẽ cần phải phát triển một ứng dụng đòi hỏi nhiều hơn một nơi chứa đối tượng liên tục. Khi nhiều nơi chứa được yêu cầu, điều phối viên “trưng bày” các nơi chứa này lên các tầng trên của ngăn xếp Core Data như là một nơi chứa duy nhất.

Persistent Object Store

Persistent object store để chỉ các môi trường lưu trữ bên dưới trong đó dữ liệu được lưu trữ khi sử dụng Core Data. Core Data hỗ trợ ba disk-based  và một memory-based. Tùy chọn trên Disk based bao gồm SQLite, XML và nhị phân. Theo mặc định, iPhone SDK sẽ sử dụng SQLite như các Disk based store. Trong thực tế, các loại store đang được sử dụng là trong suốt đối với bạn như là nhà phát triển. Bất kể sự lựa chọn của bạn về persistent store, mã của bạn sẽ làm cho các cuộc gọi đến cùng Core Data APIs để quản lý các đối tượng dữ liệu theo yêu cầu của ứng dụng của bạn.

Defining an Entity Description

Mô tả thực thể thể được xác định từ trong môi trường Xcode. Khi một dự án mới được tạo ra với các tùy chọn để include Core Data, một tập tin mẫu sẽ được tạo ra với tên <projectname>.xcdatamodel. Nhấp đôi vào tập tin này trong cửa sổ chính của dự án Xcode sẽ được tải mô hình vào trong môi trường edit thực thể, như được minh họa trong hình sau đây:

Creating a new Core Data Entity Description in Xcode
Tạo ra một thực thể mới bằng cách nhấn vào nút + nằm nút bên trái của bảng điều khiển Entity (nằm ở góc trên bên trái của cửa sổ). Các thực thể mới sẽ xuất hiện trong danh sách và bảng điều khiển chi tiết sẽ hiển thị các cài đặt cho các thực thể mới. Diện tích đồ thị cũng sẽ cập nhật để phản ánh sự tồn tại của thực thể mới mà ở đó, nếu không có gì khác, cái tên ít nhất phải được thay đổi một cái đó mô tả nhiều hơn so với mặc định (the name at least should be changed to something more descriptive than the default):

Editing the properties of a Core Data entity description
Để thêm các thuộc tính đến thực thể, hãy nhấp vào nút + nằm ở góc dưới bên trái của bảng điều khiển Property (nằm phía trên, trung tâm của cửa sổ) chọn Add Attribute từ menu kết quả. Trong bảng điều khiển chi tiết, tên thuộc tính và chỉ định kiểu các lựa chọn khác được yêu cầu:

Defining attributes for a Core Data entity
Lặp lại các bước trên để thêm nhiều hơn những thuộc tính và các thực thể.


 Môi trường thực thể Xcode cũng cho phép các mối quan hệ được thiết lập giữa các thực thể. Giả sử, ví dụ, hai thực thể có tên là Contacts Sales. Để thiết lập một mối quan hệ giữa hai bảng, click chuột phải vào hộp Contacts trong khu vực đồ thị và chọn Add Relationship từ menu. Trong tên bảng chi tiết tên các mối quan hệ, xác định target cho thực thể Sales bất kỳ tùy chọn khác được yêu cầu cho mối quan hệ. Các biểu đồ sẽ cập nhật để phản ánh mối quan hệ:

Defining a relationship in a Core Data entity

Như đã chứng minh, Xcode làm cho quá trình tả thực thể khá đơn giản. Trong khi một tổng quan chi tiết của quá trình này vượt quá phạm vi của cuốn sách này, nhiều nguồn tài liệu khác có sẵn được dành riêng cho các chủ đề.

Obtaining the Managed Object Context

Kể từ khi nhiều phương thức Core Data yêu cầu managed object context như một tham số, bước tiếp theo sau khi xác định mô tả thực thể thường liên quan đến việc chứa một tham chiếu đến context. Điều này đạt được bằng cách xác định application delegate và sau đó gọi phương thức managedContextObject của đối tượng delegate:

coreDataAppDelegate *appDelegate = [[UIApplication sharedApplication] 
      delegate];
 
NSManagedObjectContext *context = [appDelegate managedObjectContext];

Getting an Entity Description

Trước khi managed objects có thể được tạo ra thao tác trong mã, mô tả thực thể tương ứng đầu tiên phải được nạp. Điều này đạt được bằng cách gọi phương thức entityForName của lớp NSEntityDescription, truyền thông qua tên của thực thể được yêu cầu context như các đối số và sau đó tạo ra một fetch request. Các đoạn mã sau đây chứa mô tả cho một thực thể với tên Contacts:

NSEntityDescription *entityDesc = [NSEntityDescription    
     entityForName:@"Contacts" inManagedObjectContext:context];
 
NSFetchRequest *request = [[NSFetchRequest alloc] init];
 
[request setEntity:entityDesc]; 

Creating a Managed Object

Having obtained the managed context, một managed object  mới phù hợp với một mô tả thực thể được xác định có thể được tạo ra bằng cách tham chiều đến context và mô tả tên thực thể:
NSManagedObject *newContact;
 
newContact = [NSEntityDescription insertNewObjectForEntityForName:@"Contacts"   
       inManagedObjectContext:context];
 
NSError *error;
 
[context save:&error];

Getting and Setting the Attributes of a Managed Object

Như thảo luận trước đó, các thực thể các managed object mà từ đó chúng được bắt đầu chứa dữ liệu trong các form của các thuộc tính. Những thuộc tính này được lưu trữ trong các đối tượng bằng cách sử dụng một value-key để mã hóa hệ thống, theo đó key này được tham chiếu để lấy hoặc thiết lập các thuộc tính tương ứng. Giả sử một managed object tên newContact với những thuộc tính chỉ định các khóa tên, địa chỉ số điện thoại tương ứng, các giá trị của các thuộc tính này thể được thiết lập bằng cách sử dụng phương thức setValue của thể hiện của lớp NSManagedObject:

[newContact setValue:@”John Smith” forKey:@"name"];
[newContact setValue:@”123 The Street” forKey:@"address"];
[newContact setValue:@”555-123-1234” forKey:@"phone"];
 
Ngược lại, giá trị hiện tại cho một khóa tương ứng thể được truy cập bằng cách sử dụng phương pháp valueForKey của managed object:

NSString *contactname = [newcontact valueForKey:@”name”];
 
Dòng code trên trích giá trị hiện tại cho các thuộc tính tên của managed object  newcontact quản lý đối tượng gán nó vào một đối tượng chuỗi.

Fetching Managed Objects

Một khi managed object được lưu vào persistent object store thì nó rất có khả năng các đối tượng đó dữ liệu chúng chứa sẽ cần phải được lấy. Các đối tượng được lấy ra bằng cách thực hiện một fetch request được trả về trong form của một đối tượng NSArray. Các mã sau đây giả định rằng cả hai context và mô tả thực thể đã thu được trước khi thực hiện fetch request:

NSFetchRequest *request = [[NSFetchRequest alloc] init];
 
[request setEntity:entityDesc];
 
NSError *error;
 
NSArray *matching_objects = [context executeFetchRequest:request error:&error];
 
[request release];
 
Sau khi thực hiện, các mảng matching_objects sẽ chứa tất cả các managed object được lấy theo yêu cầu.

Retrieving Managed Objects based on Criteria

Ví dụ trước lấy ra tất cả các đối tượng được quản lý từ các persistent object store cho một thực thể được chỉ định. More often than not only managed objects that match specified criteria are required during a retrieval operation. Điều này được thực hiện bằng cách định nghĩa một vị ngữ mà nó xác định chuẩn mà managed object phải được đáp ứng để có đủ điều kiện để lấy. Ví dụ, mã sau đây thực hiện một vị ngữ để chỉ trích xuất các đối tượng quản lý mà ở đó các thuộc tính tên gắn với "John Smith":

NSFetchRequest *request = [[NSFetchRequest alloc] init];
 
[request setEntity:entityDesc];
 
NSPredicate *pred = [NSPredicate predicateWithFormat:@"(name = %@)", “John Smith”];
 
[request setPredicate:pred];
 
NSError *error;
 
NSArray *matching_objects = [context executeFetchRequest:request error:&error]; 
[request release];
  

Summary

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ư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.

Saturday, September 1, 2012

[Bài Học Số 3] Cơ Bản Lập Trinh Objective C



 http://congdongios.com/showthread.php?1448

Quản lý bộ nhớ
Quản lý bộ nhớ là tiến trình xử lý việc khởi tạo vùng nhớ cho ứng dụng, sử dụng nó và giải phóng nó khi ta đã hoàn thành công việc. Một chương trình quản lý bộ nhớ tốt sẽ sử dụng ít vùng nhớ nhất có thể. Quản lý bộ nhớ là một vấn đề rất quan trọng trong lập trình. Một vấn đề thường hay gặp phải đó là leak memory, khi chúng ta khởi tạo mà không giải phóng vùng nhớ. Điều này có thể dẫn tới việc lãng phí bộ nhớ hoặc nghiêm trọng hơn là out of memory và dẫn tới crash chương trình.

Objective-C cung cấp 3 cách quản lý bộ nhớ
• Manual retain-release (MRR) hoặc có thể gọi là Manual Reference Counting (MRC) có nghĩa là chúng ta sẽ tự quản lý bộ nhớ bằng cách đánh dấu quá trình sử dụng của các object mà ta có. Cụ thể là ta sẽ đếm việc khởi tạo, sử dụng và giải phóng object trong chương trình.
• Autiomatic Reference Counting (ARC), hệ thống sẽ dùng bộ đếm tương tự như MRR nhưng nó sẽ thêm vào method quản lý bộ nhớ tự động tại thời điểm compile. Ta nên sử dụng ARC cho các project mới
• Garbage Collection (GC), hệ thống sẽ tự động lưu vết và tự động giải phóng vùng nhớ của object không cần thiết nữa. Nó sử dụng kỹ thuật quản lý bộ nhớ khác MRR hoặc ARC tuy nhiên GC chỉ hỗ trợ Mac OS X mà không hỗ trợ iOS

Reference Counting
Khi một object được sinh ra, Objective-C sử dụng một kỹ thuật gọi là reference counting hoặc đôi khi gọi là retain counting để theo vết vòng đời của object. Mỗi object sẽ có một số interger đánh dấu nó, được biết như là reference count hoặc retain count. Khi một đoạn code nào đó tác động tới object, đoạn code đó sẽ tăng retain count, khi kết thúc đoạn code và không sử dụng object nữa, nó sẽ giảm retain count. Khi retain count về 0 cũng có nghĩa là nó không sử dụng nữa và lúc này nó sẽ bị hủy khỏi bộ nhớ.

Khi object được khởi tạo bằng các từ khóa như alloc, new hoặc xử lý message copy, retain count của object sẽ tự động là 1. Để tăng retain count lên, ta gửi retain message cho object. Để giảm retain count ta gửi release message.

Khi retain count của object về 0 và object cần được hủy, Objective-C sẽ tự động gửi dealloc message cho object. Ta có thể override dealloc method trong object để release resource nào mà ta muốn. Không nên gọi dealloc method trực tiếp. Để có thể biết được retain count hiện tại, ta gửi retainCount message. Tóm lại ta có 3 method quan trọng như sau
PHP Code:
PHP Code:
-    (idretain
-    (
voidrelease
-    (
unsignedretainCount



retain trả về id nên ta có thể kết hợp nó với chuỗi message ví dụ [[car retain] setTire:tire atIndex:2]; báo cho object car tăng retain count và gửi message setTire. Ta coi ví dụ sau

PHP Code:
PHP Code:
@interface RetainTracker NSObject 
   
@end 

   
@implementation RetainTracker 
   
-(id)init 
   

      if (
self = [super init]) 
      { 
         
NSLog(@”init”); 
      } 
      return 
self
   } 

   -(
void)dealloc 
   

      
NSLog(“dealloc”); 
      [
super dealloc]; 
   } 
   @
end 


Object RetainTracker trong ví dụ trên override lại dealloc methods để xử lý cần thiết khi object bị hủy, tiếp theo ta sẽ coi vòng đời của object RetainTracker thông qua các methods retain và alloc trong ví dụ dưới.

PHP Code:
PHP Code:
int main(int argc, const char *argv[]) 
   { 
      
RetainTracker *tracker = [RetainTracker new]; 
      
//count: 1 

      
[tracker retain];     // count: 2 
      
[tracker retain];     // count: 2 
      
[tracker release];   // count: 1 
      
[tracker retain];     // count: 2  
      
[tracker release];   // count: 1 
      
[tracker release];   // count: 0, dealloc 
      
return (0); 
   } 

Qua ví dụ trên, ta thấy rõ cơ cấu quản lý bộ nhớ của Objective-C như thế nào.


Object Ownership
Cho tới giờ việc quản lý bộ nhớ mà ta biết chưa có gì quá phức tạp, nó đi theo chu trình từ lúc khởi tạo thông qua bộ đếm retain count và dealloc khi về 0. Vấn đề sẽ thực sự trở nên rắc rối khi ta đề cập tới khái niệm object ownership.

Một object với instance variables trỏ tới một object khác được gọi là own object khác. Ta có object CarParts có các variables engine trỏ tới object engine và tires trỏ tới các objects tires. Ví dụ trong object CarParts có methods

PHP Code:
-(void)setEngine: (Engine*) newEngine;


Và trong hàm main() ta gọi

PHP Code:
PHP Code:
Engine *engine = [Engine new]; 
      [
car setEngineengine]; 



Trong trường hợp trên ta sẽ gặp rắt rối nảy sinh khi object engine vừa nằm trong hàm main() vừa được sử dụng trong object car (kiểu CarParts) và khó xác định khi nào thực sự object engine bị dealloc. Nếu ta release trong hàm main() thì object engine sẽ bị xóa trong khi CarParts vẫn đang sử dụng, tương tự nếu release trong CarParts object nhưng hàm main() vẫn còn cần nó.

Cách giải quyết vấn đề trên là ta sẽ tăng retain count của engine lên 2 ngay khi object CarParts sử dụng nó. Như vậy retain count của engine là 2 và khi ta release khi CarParts không sử dụng nó nữa sau đó release tại main() thì retain count của engine về 0 sẽ được dealloc một cách hợp lý.

Retain và Release trong Methods
Đầu tiên ta nói kỹ hơn một chút về object CarParts, nó có chứa một instance variable engine kiểu Engine* và hàm setEngine được gọi là một accessor method (coi phần Property) có chức năng gán object cho engine. Ngoài ra trong dealloc method của object CarParts có xử lý cho phần dealloc của engine variables. Quay lại ví dụ ở phần trước, ta sẽ coi cách xử lý trong method setEngine như thế nào:

PHP Code:
PHP Code:
-(void)setEngine: (Engine *)newEngine 
   

      
engine=[newEngine retain];    //chú ý retain trả về id của Engine 
   



Cách code như trên đã thõa đúng cách giải quyết mà ta đưa ra ban đầu, newEngine (tức là engine mà main method đưa vào) được tăng retain count trong accessor method. Khi dealloc method trong CarParts được gọi thì engine sẽ được release để retain count giảm xuống như phương pháp ta đã nêu ở phía trên. Tuy nhiên cách code này có 1 vấn đề là nếu engine variable đang trỏ tới một object Engine khác (tức là ta đã gửi message method setEngine trước đó) thì object đó sẽ không được release khi engine trỏ sang object Engine mới, điều này làm leak object Engine cũ, do đó cách code như trên là không tối ưu.

Để khắc phục tình trạng ở phía trên, ta xem thử đoạn code dưới đây

PHP Code:
PHP Code:
-(void)setEngine: (Engine *)newEngine 
   

      [
engine release]; 
      
engine=[newEngine retain];    //chú ý retain trả về id của Engine 
   



Cách code trên đưa ra giải pháp release ngay engine variable trước khi nhận object Engine mới. Nhìn có vẻ hợp lý, tuy nhiên đoạn code trên sẽ gặp vấn đề cực kỳ lớn nếu như ta gặp phải đoạn code dưới đây

PHP Code:
PHP Code:
Engine *engine = [Engine new];   //count: 1 
   
Car *car1 = [Car new]; 
   
Car *car2 = [Car new]; 

   [
car1 setEngineengine];   //count: 2 
   
[engine release];  //count: 1 

   
[car2 setEngine: [car1 engine]];   //count: 0 


AutoRelease

AutoRelease là cơ chế mà ta không cần phải quan tâm đến việc release đối tương, nó sẽ tự động giảm retain count xuống và release khi ta không sử dụng nó nữa. Để khai báo autorelease ta sử dụng như sau :

PHP Code:
[PHP]Car *car1 = [[[Car alloc] init] autorelease];


[/PHP]

Tuy nhiên chúng ta nên cẩn thận đối với loại này. Ví dụ hàm ta có 1 đối tượng car1 trong class và nó được khai báo như trên trong phương thức A(), ở phương thức B() ta sử dụng đối tượng này sẽ bị crash do khi chạy hết hàm A() thì đối tượng car1 đã bị release.

Protocols
Khai báo các protocols methods có thể được implement trong bất cứ class nào. Protocols có thể hữu ích trong ba trường hợp sau:
• Khai báo method mà class khác dự định sẽ thực thi
• Khai báo interface cho một object và che dấu nội dung bên trong nó
• Dễ dàng nắm bắt được cấu trúc nhiều class tương đồng có liên quan (khi sử dụng chung interface)

Properties
Khai báo properties là một chức năng Objective-C cung cấp để ta có thể khai báo và truy cập dễ dàng vào các giá trị của object thông qua các accessor methods.

Khái quát
Ta chủ yếu có thể truy cập vào properties của object thông qua cặp method truy cập (accessor methods) được gọi là getter và setter. Sử dụng properties mang lại cho ta rất nhiều thuận lợi:
• Cung cấp một cái nhìn rõ ràng cho người dùng, hiểu được cách thức tương tác với API.
• Compiler có thể tổng hợp các accessor methods (getter/setter) mà ta đã khai báo


Khai báo và thực thi Property
Trong phần này sẽ có 2 phần là khai báo và thực thi property

Khai báo Property
Một property được khai báo bắt đầu bằng từ khóa @property. @property có thể xuất hiện bất cứ ở đâu trong danh sách khai báo các methods. @property cũng có xuất hiện trong phần khai báo protocol và category.

PHP Code:
@property (attribute) type name;


Từ khóa @property khai báo một property. Giống như các dạng khác trong Objective-C, mỗi property có một kiểu dữ liệu và một cái tên. Ngoài ra như ví dụ trên ta thấy còn có tùy chọn các thuộc tính attribute thêm trong dấu ngoặc trong trường hợp đặc biệt, ta sẽ coi ở phần bên dưới.

Tiếp theo chúng ta coi ví dụ đơn giản khai báo một property

PHP Code:
PHP Code:
@interface MyClass NSObject
   
@property float value;
   @
end 


Trong ví dụ trên thì dòng thứ 2 ta khai báo một property tên value kiểu float. Nó tương đương với dòng lệnh
PHP Code:
PHP Code:
-(float)value;
   -(
void)setValue:(float)newValue



Khai báo Property Attributes
Chúng ta có thể thêm vào một property các attributes theo cú pháp @property(attribute1, attribute2,…). Nếu ta dùng @synthesize (trong phần implementation) để nói cho compiler tạo các accessor methods truy cập property, compiler sẽ tự động xử lý hoàn toàn. Tuy nhiên nếu ta tự xử lý accessor methods, ta phải chắc rằng nó phù hợp với những gì ta đã khai báo trong interface, kể cả các attributes. Ta sẽ coi rõ trong phần implementation.

Accessor Method Names
Tên mặc định của getter và setter methods với property là PropertyName và setPropertyName. Ví dụ như property có tên foo thì getter method là foo và setter method là setFoo. Tuy nhiên ta có thể chỉnh sữa việc này trong property attribute với attributes getter= và setter=. Hai attributes này có thể kết hợp với các attribute khác một cách dễ dàng ngoại trừ attribute readonly, vì khi đó ta không được xài setter=. Chú ý là khi ta xài hai attributes này thì trong phần implementation phải xử lý thích hợp.

PHP Code:
getter=getterName


Khai báo trước tên accessor methods getter mà ta phải thực thi trong implementation. Chú ý getter method ko chứa parametters

PHP Code:
setter=setterName


Khai báo trước tên accessor methods setter mà ta phải thực thi trong implementation. Chú ý setter method ko chứa parametters và không đi cùng với attribute readonly.

Wriability
Những attributes này quyết định khả năng read/write của property, ta có thể quyết định một property có thể read hay write hay không qua các attributes này

PHP Code:
PHP Code:
readwrite :Property sẽ có thể vừa read vừa writeđây là attribute mặc định
readonly 
Property chỉ có thể readsetter method trong implementation là không có



Thực thi Property
Trong phần implementation, ta có thể dùng @synthesize và @dynamic để báo cho compiler thực thi các property mà ta đã khai báo trên interface. Chú ý là nếu ta không sử dụng các từ kháo trên, ta sẽ phải tự thực thi các getter/setter methods một cách chính xác hoàn toàn (theo các properties đã khai báo kèm).

@synthesize
Chúng ta dùng @synthesize để nói compiler tạo ngầm các setter/getter methods cho property nếu chúng ta không có khai báo chúng trong @implementation. Ta coi ví dụ

PHP Code:
PHP Code:
@interface MyClass NSObject
   
@property(copyreadwriteNSString *value;
   @
end

   
@implementation MyClass
   
@synthesize value;
   @
end 


Ta có thể dùng lệnh gán để chỉ định instance variable đặc biệt nào được dùng cho property. Ví dụ

PHP Code:
@synthesize firstName, lastName, age=yearOld;


Dòng lệnh trên tạo các accessor methods cho firstName, lastName và age trong đó giá trị của property age là giá trị của instance variable yearOld. Mặc khác @synthesize sẽ thiết lập các accessor method phù hợp với các attributes mà ta khai báo cho properties.

@dynamic
Một vài trường hợp accessor methods được tạo động lúc runtime, khi đó ta dùng từ khóa @dynamic để thông báo với hệ thống rằng các accessor methods sẽ được tạo động trong lúc runtime. Ta sẽ sử dụng nó và coi kỹ hơn khi thao tác với NSManagedObject.