[Cocoa]深入淺出 Cocoa 之 Core Data(2)- 手動編寫代碼

來源:互聯網
上載者:User

深入淺出 Cocoa 之 Core Data(2)- 程式碼範例

羅朝輝(http://blog.csdn.net/kesalin)

CC 許可,轉載請註明出處

前面詳細講解了 Core Data 的架構以及設計的類,下面我們來講解一個完全手動編寫代碼使用這些類的樣本,這個例子來自蘋果官方樣本。在這個例子裡面,我們打算做這樣一件事情:記錄程式運行記錄(時間與 process id),並儲存到xml檔案中。我們使用 Core Data 來做這個事情。

範例程式碼下載:點擊這裡

一,建立一個新的 Mac command-line tool application 工程,命名為 CoreDataTutorial。為支援垃圾主動回收機制,點擊項目名稱,在右邊的 Build Setting 中尋找 garbage 關鍵字,將找到的 Objective-C Garbage Collection 設定為 Required [-fobj-gc-only]。並將  main.m 中 的 main() 方法修改為如下:

int main (int argc, const char * argv[]){    NSLog(@" === Core Data Tutorial ===");    // Enable GC    //    objc_startCollectorThread();        return 0;}

二,建立並設定模型類

在 main() 之前添加如下方法:

NSManagedObjectModel *managedObjectModel(){    static NSManagedObjectModel *moModel = nil;    if (moModel != nil) {        return moModel;    }        moModel = [[NSManagedObjectModel alloc] init];        // Create the entity    //    NSEntityDescription *runEntity = [[NSEntityDescription alloc] init];    [runEntity setName:@"Run"];    [runEntity setManagedObjectClassName:@"Run"];        [moModel setEntities:[NSArray arrayWithObject:runEntity]];        // Add the Attributes    //    NSAttributeDescription *dateAttribute = [[NSAttributeDescription alloc] init];    [dateAttribute setName:@"date"];    [dateAttribute setAttributeType:NSDateAttributeType];    [dateAttribute setOptional:NO];        NSAttributeDescription *idAttribute = [[NSAttributeDescription alloc] init];    [idAttribute setName:@"processID"];    [idAttribute setAttributeType:NSInteger32AttributeType];    [idAttribute setOptional:NO];    [idAttribute setDefaultValue:[NSNumber numberWithInteger:-1]];    // Create the validation predicate for the process ID.    // The following code is equivalent to validationPredicate = [NSPredicate predicateWithFormat:@"SELF > 0"]    //    NSExpression *lhs = [NSExpression expressionForEvaluatedObject];    NSExpression *rhs = [NSExpression expressionForConstantValue:[NSNumber numberWithInteger:0]];        NSPredicate *validationPredicate = [NSComparisonPredicate                                        predicateWithLeftExpression:lhs                                        rightExpression:rhs                                        modifier:NSDirectPredicateModifier                                        type:NSGreaterThanPredicateOperatorType                                        options:0];        NSString *validationWarning = @"Process ID < 1";    [idAttribute setValidationPredicates:[NSArray arrayWithObject:validationPredicate]                  withValidationWarnings:[NSArray arrayWithObject:validationWarning]];        // set the properties for the entity.    //    NSArray *properties = [NSArray arrayWithObjects: dateAttribute, idAttribute, nil];    [runEntity setProperties:properties];        // Add a Localization Dictionary    //    NSMutableDictionary *localizationDictionary = [NSMutableDictionary dictionary];    [localizationDictionary setObject:@"Date" forKey:@"Property/date/Entity/Run"];    [localizationDictionary setObject:@"Process ID" forKey:@"Property/processID/Entity/Run"];    [localizationDictionary setObject:@"Process ID must not be less than 1" forKey:@"ErrorString/Process ID < 1"];        [moModel setLocalizationDictionary:localizationDictionary];        return moModel;}

在上面的代碼中:

1)我們建立了一個全域模型 moModel;
2)並在其中建立一個名為 Run 的 Entity,這個 Entity 對應的 ManagedObject 類名為 Run(很快我們將建立這樣一個類);
3)給 Run Entity 添加了兩個必須的 Property:date 和 processID,分別表示已耗用時間以及進程 ID;並設定預設的進程 ID 為 -1;
4)給 processID 特性設定檢驗條件:必須大於 0;
5)給模型設定本地化描述詞典;

本地化描述提供對 Entity,Property,Error資訊等的便於理解的描述,其可用的索引值對如下表:

Key

Value


"Entity/NonLocalizedEntityName"

"LocalizedEntityName"

"Property/NonLocalizedPropertyName/Entity/EntityName"

"LocalizedPropertyName"

"Property/NonLocalizedPropertyName"

"LocalizedPropertyName"

"ErrorString/NonLocalizedErrorString"

"LocalizedErrorString"

三,建立並設定運行時類和對象

由於要用到儲存功能,所以我們必須定義持久化資料的儲存路徑。我們在 main() 之前添加如下方法設定儲存路徑:

NSURL *applicationLogDirectory(){    NSString *LOG_DIRECTORY = @"CoreDataTutorial";    static NSURL *ald = nil;        if (ald == nil)    {        NSFileManager *fileManager = [[NSFileManager alloc] init];        NSError *error = nil;        NSURL *libraryURL = [fileManager URLForDirectory:NSLibraryDirectory inDomain:NSUserDomainMask                                       appropriateForURL:nil create:YES error:&error];        if (libraryURL == nil) {            NSLog(@"Could not access Library directory\n%@", [error localizedDescription]);        }        else        {            ald = [libraryURL URLByAppendingPathComponent:@"Logs"];            ald = [ald URLByAppendingPathComponent:LOG_DIRECTORY];                        NSLog(@" >> log path %@", [ald path]);                        NSDictionary *properties = [ald resourceValuesForKeys:[NSArray arrayWithObject:NSURLIsDirectoryKey] error:&error];            if (properties == nil)            {                if (![fileManager createDirectoryAtPath:[ald path] withIntermediateDirectories:YES attributes:nil error:&error])                {                    NSLog(@"Could not create directory %@\n%@",                          [ald path], [error localizedDescription]);                    ald = nil;                }            }        }    }        return ald;}

在上面的代碼中,我們將持久化資料檔案儲存到路徑:/Users/kesalin/Library/Logs/CoreDataTutorial 下。

下面,我們來建立運行時對象:ManagedObjectContext 和 PersistentStoreCoordinator。

NSManagedObjectContext *managedObjectContext(){    static NSManagedObjectContext *moContext = nil;    if (moContext != nil) {        return moContext;    }        moContext = [[NSManagedObjectContext alloc] init];        // Create a persistent store coordinator, then set the coordinator for the context.    //    NSManagedObjectModel *moModel = managedObjectModel();    NSPersistentStoreCoordinator *coordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:moModel];    [moContext setPersistentStoreCoordinator: coordinator];        // Create a new persistent store of the appropriate type.     //    NSString *STORE_TYPE = NSXMLStoreType;    NSString *STORE_FILENAME = @"CoreDataTutorial.xml";        NSError *error = nil;    NSURL *url = [applicationLogDirectory() URLByAppendingPathComponent:STORE_FILENAME];        NSPersistentStore *newStore = [coordinator addPersistentStoreWithType:STORE_TYPE                                                            configuration:nil                                                                      URL:url                                                                  options:nil                                                                    error:&error];        if (newStore == nil) {        NSLog(@"Store Configuration Failure\n%@", ([error localizedDescription] != nil) ? [error localizedDescription] : @"Unknown Error");    }    return moContext;}

在上面的代碼中:
1)我們建立了一個全域 ManagedObjectContext 對象 moContext;
2)並在設定其 persistent store coordinator,儲存類型為 xml,儲存檔案名稱為:CoreDataTutorial.xml,並將其放到前面定義的儲存路徑下。

好,至此萬事具備,只欠 ManagedObject 了!下面我們就來定義這個資料對象類。向工程添加 Core Data->NSManagedObject subclass 的類,名為 Run (模型中 Entity 定義的類名) 。

Run.h

#import <CoreData/NSManagedObject.h>@interface Run : NSManagedObject{    NSInteger processID;}@property (retain) NSDate *date;@property (retain) NSDate *primitiveDate;@property NSInteger processID;@end

Run.m

////  Run.m//  CoreDataTutorial////  Created by kesalin on 8/29/11.//  Copyright 2011 kesalin@gmail.com. All rights reserved.//#import "Run.h"@implementation Run@dynamic date;@dynamic primitiveDate;- (void) awakeFromInsert{    [super awakeFromInsert];    self.primitiveDate = [NSDate date];}#pragma mark -#pragma mark Getter and setter- (NSInteger)processID {    [self willAccessValueForKey:@"processID"];    NSInteger pid = processID;    [self didAccessValueForKey:@"processID"];    return pid;}- (void)setProcessID:(NSInteger)newProcessID{    [self willChangeValueForKey:@"processID"];    processID = newProcessID;    [self didChangeValueForKey:@"processID"];}// Implement a setNilValueForKey: method. If the key is “processID” then set processID to 0.//- (void)setNilValueForKey:(NSString *)key {        if ([key isEqualToString:@"processID"]) {        self.processID = 0;    }    else {        [super setNilValueForKey:key];    }}@end

注意:
1)這個類中的 date 和 primitiveDate 的訪問屬性為 @dynamic,這表明在運行期會動態產生對應的 setter 和 getter;
2)在這裡我們示範了如何正確地手動實現 processID 的 setter 和 getter:為了讓 ManagedObjecContext  能夠檢測 processID的變化,以及自動支援 undo/redo,我們需要在訪問和更改資料對象時告之系統,will/didAccessValueForKey 以及 will/didChangeValueForKey 就是起這個作用的。
3)當我們設定 nil 給資料對象 processID 時,我們可以在 setNilValueForKey 捕獲這個情況,並將 processID  置 0;
4)當資料對象被插入到 ManagedObjectContext 時,我們在 awakeFromInsert 將時間設定為目前時間。

三,建立或讀取資料對象,設定其值,儲存
好,至此真正的萬事具備,我們可以建立或從持久化檔案中讀取資料對象,設定其值,並將其儲存到持久化檔案中。本例中持久化檔案為 xml 檔案。修改 main() 中代碼如下:

int main (int argc, const char * argv[]){    NSLog(@" === Core Data Tutorial ===");    // Enable GC    //    objc_startCollectorThread();    NSError *error = nil;        NSManagedObjectModel *moModel = managedObjectModel();    NSLog(@"The managed object model is defined as follows:\n%@", moModel);        if (applicationLogDirectory() == nil) {        exit(1);    }        NSManagedObjectContext *moContext = managedObjectContext();        // Create an Instance of the Run Entity    //    NSEntityDescription *runEntity = [[moModel entitiesByName] objectForKey:@"Run"];    Run *run = [[Run alloc] initWithEntity:runEntity insertIntoManagedObjectContext:moContext];    NSProcessInfo *processInfo = [NSProcessInfo processInfo];    run.processID = [processInfo processIdentifier];        if (![moContext save: &error]) {        NSLog(@"Error while saving\n%@", ([error localizedDescription] != nil) ? [error localizedDescription] : @"Unknown Error");        exit(1);    }        // Fetching Run Objects    //    NSFetchRequest *request = [[NSFetchRequest alloc] init];    [request setEntity:runEntity];    NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"date" ascending:YES];    [request setSortDescriptors:[NSArray arrayWithObject:sortDescriptor]];        error = nil;    NSArray *array = [moContext executeFetchRequest:request error:&error];    if ((error != nil) || (array == nil))    {        NSLog(@"Error while fetching\n%@", ([error localizedDescription] != nil) ? [error localizedDescription] : @"Unknown Error");        exit(1);    }        // Display the Results    //    NSDateFormatter *formatter = [[NSDateFormatter alloc] init];    [formatter setDateStyle:NSDateFormatterMediumStyle];    [formatter setTimeStyle:NSDateFormatterMediumStyle];        NSLog(@"%@ run history:", [processInfo processName]);        for (run in array)    {        NSLog(@"On %@ as process ID %ld", [formatter stringForObjectValue:run.date], run.processID);    }        return 0;}

在上面的代碼中:
1)我們先獲得全域的 NSManagedObjectModel 和 NSManagedObjectContext 對象:moModel 和 moContext;
2)並建立一個Run Entity,設定其 Property processID 為當前進程的 ID;
3)將該資料對象儲存到持久化檔案中:[moContext save: &error]。我們無需與 PersistentStoreCoordinator 打交道,只需要給 ManagedObjectContext 發送 save 訊息即可,NSManagedObjectContext 會透明地在後面處理對持久化資料檔案的讀寫;
4)然後我們建立一個 FetchRequest 來查詢持久化資料檔案中儲存的資料記錄,並將結果按照日期升序排列。查詢操作也是由 ManagedObjectContext 來處理的:[moContext
executeFetchRequest:request error:&error];
5)將查詢結果列印輸出;

大功告成!編譯運行,我們可以得到如下顯示:

2011-09-03 21:42:47.556 CoreDataTutorial[992:903] CoreDataTutorial run history:2011-09-03 21:42:47.557 CoreDataTutorial[992:903] On 2011-9-3 下午09:41:56 as process ID 9402011-09-03 21:42:47.557 CoreDataTutorial[992:903] On 2011-9-3 下午09:42:16 as process ID 9552011-09-03 21:42:47.558 CoreDataTutorial[992:903] On 2011-9-3 下午09:42:20 as process ID 9652011-09-03 21:42:47.558 CoreDataTutorial[992:903] On 2011-9-3 下午09:42:24 as process ID 9782011-09-03 21:42:47.559 CoreDataTutorial[992:903] On 2011-9-3 下午09:42:47 as process ID 992

通過這個例子,我們可以更好理解 Core Data  的運作機制。在 Core Data 中我們最常用的就是 ManagedObjectContext,它幾乎參與對資料對象的所有操作,包括對 undo/redo 的支援;而 Entity 對應的運行時類為 ManagedObject,我們可以理解為抽象資料結構 Entity 在記憶體中由 ManagedObject 來體現,而 Perproty 資料類型在記憶體中則由 ManagedObject 類的成員屬性來體現。一般我們不需要與 PersistentStoreCoordinator
打交道,對資料檔案的讀寫操作都由 ManagedObjectContext 為我們代勞了。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.