01
Jan
用Core Data收藏歷久彌新的宋詞
Facebook

在App 裡儲存大量資料時,資料庫無疑是最佳的解決方案。iOS SDK 在這方面的支援無庸置疑,它提供了SQLite 方便熟悉資料庫的專家使用。然而,並非人人都是資料庫專家,繁雜的資料庫語法也頗為難記。於是,Apple 貼心地提供了Core Data Framework,隱藏深奧的資料庫操作細節,提供開發者一套簡單易懂,方便操作的API。在這期的專欄,彼得潘將以歷久彌新的宋詞為例,將一篇篇問世間情是何物,直教人生死相許的詞兒, 用Core Data 收藏得好好的。

撰文:彼得潘

 

建立支援Core Data 的專案

建立Master-Detail Application 專案CoreDataDemo。記得勾選Use Core Data,如此專案將自動包含支援Core Data 所需的程式碼。

勾選Core Data 的Master-Detail Application 專案,預設將在表格上呈現從資料庫所抓取的資料,並提供了新增和移除資料的功能。換句話說,大部分我們需要的資料庫相關功能,讀取、新增和移除,它都把我們寫好了。我們只要定義自己所需的表格欄位,修改一丁點的程式碼,即可迅速完成完美結合資料庫功能的App。 

自訂儲存宋詞的表格

在以資料庫做為我們儲存詞兒的藏寶箱時,最重要的莫過於規畫我們欲儲存的項目,再依此定義所需的表格, 以及表格的每個欄位。在彼得潘的心裡,一首宋詞最重要的資訊有以下三個項目,佳句、作者,以及作者的壽命。

在Core Data 的世界裡,所有關於表格的設定祕密,皆藏於以xcdatamodeld 作為副檔名的檔案裡。請在專案裡,找到CoreDataDemo.xcdatamodeld,觀察它的編輯畫面。 

定義表格的第一步,就好像新生的寶寶一樣,必須給它取個名字。 在Core Data 的術語裡,表格對應到Entity, 如圖所示,CoreDataDemo.xcdatamodeld 預設即為我們建立一個名為Event 的entity。

點選它後可在右邊的視窗看到此表格(Entity)所具有的欄位。從圖中我們可看到其可憐地只有一個timeStamp 欄位。

在Core Data 的術語裡, 表格欄位稱為Entity 的Attribute。當我們定義表格欄位時,最重要的莫過於名稱和型別,我們可從Attribute 欄得知欄位名稱,從Type 欄得知欄位型別。雖然專案已經貼心地幫我們預先建立Event entity,可惜它不合所需。沒關係,就讓我們一步一腳印,自己生產詞兒所需的Entity。

 

1. 點選左下角的Add Entity 按鈕幫助我們建立Entity 

2. 將新的Entity 命名為宋詞的英 文,SongCi 直接點擊Entity 的名稱即可編輯。

3. 設定表格欄位

在左邊的Entities 清單點選SongCi 後,右邊的Attributes 區塊將顯示表格SongiCi 所具有的欄位。點選Attributes 區塊左下角的+ 按鈕,即可新增欄位。

在此彼得潘新增三個欄位,personAge,personName, sentence,分別對應作者壽命,作者名字,以及感人佳句。

 

將宋詞表格轉換成類別

Core Data 的神奇之處,在於其將存取資料庫的繁複動作,以我們熟悉的物件概念加以包裝。以我們所欲珍藏的宋詞為例,若彼得潘想將蘇東坡著名的“十年生死兩茫茫"存入資料庫,只要建立宋詞物件,填入各欄位的內容,再呼叫儲存的SDK 即可。因此接著我們將學習如何把剛剛於xcdatamodeld 檔所定義的SongCi,轉化為類別,如此我們即可以此類別來產生宋詞物件。 

 

1. File -> New -> File 

在左邊的區塊點選Core Data。然後在Core Data 區塊裡選擇NSManagedObject subclass。

2. 選擇CoreDataDemo 作為我們的資料庫

3. 選擇要 生成類別的表格,SongCi 

4. 點選右下 角的Create,產 生SongCi.h 和SongCi.m 

 

SongCi.h 的主要內容如下: 

@interface SongCi : NSManagedObject 

@property (nonatomic, retain) NSNumber * personAge; 

@property (nonatomic, retain) NSString * personName; 

@property (nonatomic, retain) NSString * sentence; 

@end 

 

從SongCi.h 的內容,我們可以看到從Entity SongCi 產生的類別,具有以下特點: 

 

1. 繼承NSManagedObject 

所有我們利用Core Data 存入的資料庫物件,都繼承自NSManagedObject,擁有資料庫相關的特異功能。

 

2. 宋詞的三個欄位,sentence、psersonName 以及personAge, 皆成為SongCi 類別的property, 且具有正確的型別。String 成為NSString,Integer 32 成為NSNumber。

 

新增詞兒天不老,情難絕

在夜深人靜,一個人埋首寫著iOS App 時,著名詞人張三影的千秋歲,常常於彼得潘的腦海裡盤旋。其中的佳句“天不老,情難絕。心似雙絲網,中有千千結。"將思念之情描寫地極好。接下來彼得潘將示範如何把如此好詞存入資料庫永久保存。 

 

-(void)addSongCi 

{

AppDelegate *delegate = [[UIApplication sharedApplication] delegate];! 

NSEnt i tyDe s c r i p t i o n * e n t i t y = [ N S E nt i tyDe s c r i p t i o n entityForName:@"SongCi" inManagedObjectContext:delegate. managedObjectContext]; 

SongCi *record = [[SongCi alloc] initWithEntity:entity insertIntoManage dObjectContext:delegate.managedObjectContext]; 

record.personAge = @(30); 

record.sentence = @" 天不 老,情難絕。 心似雙絲網,中有千千結。"; 

record.personName = @" 張三影"; 

NSError *error = nil; 

[delegate.managedObjectContext save:&error]; 

 

NSEnt i tyDescription *ent i ty = [NSEnt i tyDescription entityForName:@"SongCi" inManagedObjectContext:delegate. managedObjectContext]; 

經由NSEntityDescription 物件指定我們想要讀取的表格。以下為en tityForName:inManagedObjectContext: method 的完整宣告。

+ (NSEntityDescription *)entityForName:(NSString *)entityName 

inManagedObjectContext:(NSManagedObjectContext *)context; 

在entityName 參數傳入SongCi, 指明我們想讀取的表格為SongCi。至於第二個參數content 傳入的delegate. managedObjectContext 則是我們在使用Core Data SDK 時最重要的物件。當我們在做各種資料庫的相關動作,不管是新增, 讀取還是刪除,都是透過managedObjectContext 的幫忙。至於managedObjectContext 是怎麼建立的,則完全不用我們操心。當初將專案設成支援Core Data 時,Xcode 即自動幫我們生成建立managedObjectContext 的程式碼。 

SongCi *record = [[SongCi alloc] initWithEntity:entity insertIntoMana gedObjectContext:delegate.managedObjectContext]; 

建立宋詞物件。為了初始化繼承自NSManagedObject,擁有資料庫特異功能的物件,我們必須呼叫initWithEntity:insertIntoManagedOb jectContext: 來做初始化。

record.personAge = @(30); 

record.sentence = @" 天不老,情難絕。 心似雙絲網,中有千千結。"; 

record.personName = @" 張三影" 

設定宋詞物件的內容,包含了它的年齡,名稱以及感人佳句。

[delegate.managedObjectContext save:&error]; 

將宋詞物件存入資料庫。什麼都是假的, 只有當我們呼叫managedObjectContext 的save: method 後,資料才真正寫入資料庫。

-(void)addSongCi 

{

AppDelegate *delegate = [[UIApplication sharedApplication] delegate];! 

NSEnt i tyDe s c r i p t i o n * e n t i t y = [ N S E nt i tyDe s c r i p t i o n entityForName:@"SongCi" inManagedObjectContext:delegate. managedObjectContext]; 

SongCi *record = [[SongCi alloc] initWithEntity:entity insertIntoManage dObjectContext:delegate.managedObjectContext]; 

record.personAge = @(30); 

record.sentence = @" 天不 老,情難絕。 心似雙絲網,中有千千結。"; 

record.personName = @" 張三影"; 

NSError *error = nil; 

[delegate.managedObjectContext save:&error]; 

 

讀取資料庫

學會了寫入後,另一件重要的事情莫過於讀取了。若是只會寫入,不懂得如何讀取,我們辛苦寫入再多的內容都沒有人看得到。

 

-(void)getData 

{

AppDelegate *delegate = (AppDelegate*)[[UIApplication sharedApplication] delegate]; 

NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init]; 

NSEnt i tyDe s c r i p t i o n * e n t i t y = [ N S E nt i tyDe s c r i p t i o n entityForName:@"SongCi" inManagedObjectContext:delegate. managedObjectContext]; 

[fetchRequest setEntity:entity]; 

NSError *error = nil; 

NSArray *array = [delegate.managedObjectContext executeFetchReque st:fetchRequest error:&error]; 

for(SongCi *songCi in array) 

{

NSLog(@"songci %@", songCi); 

}

 

NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init]; 

建立NSFetchRequest 物件,透過它我們才能從資料庫詢問想要查詢的資料。 

[fetchRequest setEntity:entity]; 

設定想要讀取的表格為SongCi。前 面提過NSEntityDescription 物件決定了讀取的表格,因此當我們指定fetchRequest 的entity 後, 也決定了到時候讀取的表格為SongCi。 

NSArray *array = [delegate.managedObjectContext executeFetchRe quest:fetchRequest error:&error]; 

取得SongCi 表格裡所有的宋詞物件。呼叫managedObjectContext 的executeFetchRequest:error: method 後,將回傳array 物件,裡頭包含每一個值得一讀再讀,被存入資料庫永久保存的SongCi 物件。

 

關於彼得潘

如果我會作詞作曲, 

我就能成為創作歌手。

我有一絲音感嗎?沒有。

所以,很可惜,我只能當歌手的朋友。

如果給我一天一個App 的負荷, 

也澆不熄我對蘋果的熱情。

一天能夠完成一個App 嗎?可以。

所以,是的,我是愛瘋一切為蘋果的彼得潘。

著有App 程式設計入門 (博客來電腦類Top 1) 

facebook:http://www.facebook.com/iphone.peterpan