zoukankan      html  css  js  c++  java
  • 深入浅出 Cocoa 之 Core Data(2)- 手动编写代码

    深入浅出 Cocoa 之 Core Data(2)- 代码示例

    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() 方法修改为如下:

    1. int main (int argc, const char * argv[])  
    2. {  
    3.     NSLog(@" === Core Data Tutorial ===");  
    4.   
    5.     // Enable GC  
    6.     //  
    7.     objc_startCollectorThread();  
    8.       
    9.     return 0;  
    10. }  


    二,创建并设置模型类

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

    1. NSManagedObjectModel *managedObjectModel()  
    2. {  
    3.     static NSManagedObjectModel *moModel = nil;  
    4.   
    5.     if (moModel != nil) {  
    6.         return moModel;  
    7.     }  
    8.       
    9.     moModel = [[NSManagedObjectModel alloc] init];  
    10.       
    11.     // Create the entity  
    12.     //  
    13.     NSEntityDescription *runEntity = [[NSEntityDescription alloc] init];  
    14.     [runEntity setName:@"Run"];  
    15.     [runEntity setManagedObjectClassName:@"Run"];  
    16.       
    17.     [moModel setEntities:[NSArray arrayWithObject:runEntity]];  
    18.       
    19.     // Add the Attributes  
    20.     //  
    21.     NSAttributeDescription *dateAttribute = [[NSAttributeDescription alloc] init];  
    22.     [dateAttribute setName:@"date"];  
    23.     [dateAttribute setAttributeType:NSDateAttributeType];  
    24.     [dateAttribute setOptional:NO];  
    25.       
    26.     NSAttributeDescription *idAttribute = [[NSAttributeDescription alloc] init];  
    27.     [idAttribute setName:@"processID"];  
    28.     [idAttribute setAttributeType:NSInteger32AttributeType];  
    29.     [idAttribute setOptional:NO];  
    30.     [idAttribute setDefaultValue:[NSNumber numberWithInteger:-1]];  
    31.   
    32.     // Create the validation predicate for the process ID.  
    33.     // The following code is equivalent to validationPredicate = [NSPredicate predicateWithFormat:@"SELF > 0"]  
    34.     //  
    35.     NSExpression *lhs = [NSExpression expressionForEvaluatedObject];  
    36.     NSExpression *rhs = [NSExpression expressionForConstantValue:[NSNumber numberWithInteger:0]];  
    37.       
    38.     NSPredicate *validationPredicate = [NSComparisonPredicate  
    39.                                         predicateWithLeftExpression:lhs  
    40.                                         rightExpression:rhs  
    41.                                         modifier:NSDirectPredicateModifier  
    42.                                         type:NSGreaterThanPredicateOperatorType  
    43.                                         options:0];  
    44.       
    45.     NSString *validationWarning = @"Process ID < 1";  
    46.     [idAttribute setValidationPredicates:[NSArray arrayWithObject:validationPredicate]  
    47.                   withValidationWarnings:[NSArray arrayWithObject:validationWarning]];  
    48.       
    49.     // set the properties for the entity.  
    50.     //  
    51.     NSArray *properties = [NSArray arrayWithObjects: dateAttribute, idAttribute, nil];  
    52.     [runEntity setProperties:properties];  
    53.       
    54.     // Add a Localization Dictionary  
    55.     //  
    56.     NSMutableDictionary *localizationDictionary = [NSMutableDictionary dictionary];  
    57.     [localizationDictionary setObject:@"Date" forKey:@"Property/date/Entity/Run"];  
    58.     [localizationDictionary setObject:@"Process ID" forKey:@"Property/processID/Entity/Run"];  
    59.     [localizationDictionary setObject:@"Process ID must not be less than 1" forKey:@"ErrorString/Process ID < 1"];  
    60.       
    61.     [moModel setLocalizationDictionary:localizationDictionary];  
    62.       
    63.     return moModel;  
    64. }  

    在上面的代码中:

    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() 之前添加如下方法设置存储路径:

    1. NSURL *applicationLogDirectory()  
    2. {  
    3.     NSString *LOG_DIRECTORY = @"CoreDataTutorial";  
    4.     static NSURL *ald = nil;  
    5.       
    6.     if (ald == nil)  
    7.     {  
    8.         NSFileManager *fileManager = [[NSFileManager alloc] init];  
    9.         NSError *error = nil;  
    10.         NSURL *libraryURL = [fileManager URLForDirectory:NSLibraryDirectory inDomain:NSUserDomainMask  
    11.                                        appropriateForURL:nil create:YES error:&error];  
    12.         if (libraryURL == nil) {  
    13.             NSLog(@"Could not access Library directory %@", [error localizedDescription]);  
    14.         }  
    15.         else  
    16.         {  
    17.             ald = [libraryURL URLByAppendingPathComponent:@"Logs"];  
    18.             ald = [ald URLByAppendingPathComponent:LOG_DIRECTORY];  
    19.               
    20.             NSLog(@" >> log path %@", [ald path]);  
    21.               
    22.             NSDictionary *properties = [ald resourceValuesForKeys:[NSArray arrayWithObject:NSURLIsDirectoryKey] error:&error];  
    23.             if (properties == nil)  
    24.             {  
    25.                 if (![fileManager createDirectoryAtPath:[ald path] withIntermediateDirectories:YES attributes:nil error:&error])  
    26.                 {  
    27.                     NSLog(@"Could not create directory %@ %@",  
    28.                           [ald path], [error localizedDescription]);  
    29.                     ald = nil;  
    30.                 }  
    31.             }  
    32.         }  
    33.     }  
    34.       
    35.     return ald;  
    36. }  


    在上面的代码中,我们将持久化数据文件保存到路径:/Users/kesalin/Library/Logs/CoreDataTutorial 下。

    下面,我们来创建运行时对象:ManagedObjectContext 和 PersistentStoreCoordinator。

    1. NSManagedObjectContext *managedObjectContext()  
    2. {  
    3.     static NSManagedObjectContext *moContext = nil;  
    4.     if (moContext != nil) {  
    5.         return moContext;  
    6.     }  
    7.       
    8.     moContext = [[NSManagedObjectContext alloc] init];  
    9.       
    10.     // Create a persistent store coordinator, then set the coordinator for the context.  
    11.     //  
    12.     NSManagedObjectModel *moModel = managedObjectModel();  
    13.     NSPersistentStoreCoordinator *coordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:moModel];  
    14.     [moContext setPersistentStoreCoordinator: coordinator];  
    15.       
    16.     // Create a new persistent store of the appropriate type.   
    17.     //  
    18.     NSString *STORE_TYPE = NSXMLStoreType;  
    19.     NSString *STORE_FILENAME = @"CoreDataTutorial.xml";  
    20.       
    21.     NSError *error = nil;  
    22.     NSURL *url = [applicationLogDirectory() URLByAppendingPathComponent:STORE_FILENAME];  
    23.       
    24.     NSPersistentStore *newStore = [coordinator addPersistentStoreWithType:STORE_TYPE  
    25.                                                             configuration:nil  
    26.                                                                       URL:url  
    27.                                                                   options:nil  
    28.                                                                     error:&error];  
    29.       
    30.     if (newStore == nil) {  
    31.         NSLog(@"Store Configuration Failure %@", ([error localizedDescription] != nil) ? [error localizedDescription] : @"Unknown Error");  
    32.     }  
    33.   
    34.     return moContext;  
    35. }  


    在上面的代码中:
    1)我们创建了一个全局 ManagedObjectContext 对象 moContext;
    2)并在设置其 persistent store coordinator,存储类型为 xml,保存文件名为:CoreDataTutorial.xml,并将其放到前面定义的存储路径下。

    好,至此万事具备,只欠 ManagedObject 了!下面我们就来定义这个数据对象类。向工程添加 Core Data->NSManagedObject subclass 的类,名为 Run (模型中 Entity 定义的类名) 。

    Run.h

    1. #import <CoreData/NSManagedObject.h>  
    2.   
    3. @interface Run : NSManagedObject  
    4. {  
    5.     NSInteger processID;  
    6. }  
    7.   
    8. @property (retain) NSDate *date;  
    9. @property (retain) NSDate *primitiveDate;  
    10. @property NSInteger processID;  
    11.   
    12. @end  


    Run.m

    1. //  
    2. //  Run.m  
    3. //  CoreDataTutorial  
    4. //  
    5. //  Created by kesalin on 8/29/11.  
    6. //  Copyright 2011 kesalin@gmail.com. All rights reserved.  
    7. //  
    8.   
    9. #import "Run.h"  
    10.   
    11. @implementation Run  
    12.   
    13. @dynamic date;  
    14. @dynamic primitiveDate;  
    15.   
    16. - (void) awakeFromInsert  
    17. {  
    18.     [super awakeFromInsert];  
    19.   
    20.     self.primitiveDate = [NSDate date];  
    21. }  
    22.   
    23. #pragma mark -  
    24. #pragma mark Getter and setter  
    25.   
    26. - (NSInteger)processID   
    27. {  
    28.     [self willAccessValueForKey:@"processID"];  
    29.     NSInteger pid = processID;  
    30.     [self didAccessValueForKey:@"processID"];  
    31.     return pid;  
    32. }  
    33.   
    34. - (void)setProcessID:(NSInteger)newProcessID  
    35. {  
    36.     [self willChangeValueForKey:@"processID"];  
    37.     processID = newProcessID;  
    38.     [self didChangeValueForKey:@"processID"];  
    39. }  
    40.   
    41. // Implement a setNilValueForKey: method. If the key is “processID” then set processID to 0.  
    42. //  
    43. - (void)setNilValueForKey:(NSString *)key {  
    44.       
    45.     if ([key isEqualToString:@"processID"]) {  
    46.         self.processID = 0;  
    47.     }  
    48.     else {  
    49.         [super setNilValueForKey:key];  
    50.     }  
    51. }  
    52.   
    53. @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() 中代码如下:

    1. int main (int argc, const char * argv[])  
    2. {  
    3.     NSLog(@" === Core Data Tutorial ===");  
    4.   
    5.     // Enable GC  
    6.     //  
    7.     objc_startCollectorThread();  
    8.   
    9.     NSError *error = nil;  
    10.       
    11.     NSManagedObjectModel *moModel = managedObjectModel();  
    12.     NSLog(@"The managed object model is defined as follows: %@", moModel);  
    13.       
    14.     if (applicationLogDirectory() == nil) {  
    15.         exit(1);  
    16.     }  
    17.       
    18.     NSManagedObjectContext *moContext = managedObjectContext();  
    19.       
    20.     // Create an Instance of the Run Entity  
    21.     //  
    22.     NSEntityDescription *runEntity = [[moModel entitiesByName] objectForKey:@"Run"];  
    23.     Run *run = [[Run alloc] initWithEntity:runEntity insertIntoManagedObjectContext:moContext];  
    24.     NSProcessInfo *processInfo = [NSProcessInfo processInfo];  
    25.     run.processID = [processInfo processIdentifier];  
    26.       
    27.     if (![moContext save: &error]) {  
    28.         NSLog(@"Error while saving %@", ([error localizedDescription] != nil) ? [error localizedDescription] : @"Unknown Error");  
    29.         exit(1);  
    30.     }  
    31.       
    32.     // Fetching Run Objects  
    33.     //  
    34.     NSFetchRequest *request = [[NSFetchRequest alloc] init];  
    35.     [request setEntity:runEntity];  
    36.   
    37.     NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"date" ascending:YES];  
    38.     [request setSortDescriptors:[NSArray arrayWithObject:sortDescriptor]];  
    39.       
    40.     error = nil;  
    41.     NSArray *array = [moContext executeFetchRequest:request error:&error];  
    42.     if ((error != nil) || (array == nil))  
    43.     {  
    44.         NSLog(@"Error while fetching %@", ([error localizedDescription] != nil) ? [error localizedDescription] : @"Unknown Error");  
    45.         exit(1);  
    46.     }  
    47.       
    48.     // Display the Results  
    49.     //  
    50.     NSDateFormatter *formatter = [[NSDateFormatter alloc] init];  
    51.     [formatter setDateStyle:NSDateFormatterMediumStyle];  
    52.     [formatter setTimeStyle:NSDateFormatterMediumStyle];  
    53.       
    54.     NSLog(@"%@ run history:", [processInfo processName]);  
    55.       
    56.     for (run in array)  
    57.     {  
    58.         NSLog(@"On %@ as process ID %ld", [formatter stringForObjectValue:run.date], run.processID);  
    59.     }  
    60.       
    61.     return 0;  
    62. }  


    在上面的代码中:
    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)将查询结果打印输出;

    大功告成!编译运行,我们可以得到如下显示:

    1. 2011-09-03 21:42:47.556 CoreDataTutorial[992:903] CoreDataTutorial run history:  
    2. 2011-09-03 21:42:47.557 CoreDataTutorial[992:903] On 2011-9-3 下午09:41:56 as process ID 940  
    3. 2011-09-03 21:42:47.557 CoreDataTutorial[992:903] On 2011-9-3 下午09:42:16 as process ID 955  
    4. 2011-09-03 21:42:47.558 CoreDataTutorial[992:903] On 2011-9-3 下午09:42:20 as process ID 965  
    5. 2011-09-03 21:42:47.558 CoreDataTutorial[992:903] On 2011-9-3 下午09:42:24 as process ID 978  
    6. 2011-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 为我们代劳了。

  • 相关阅读:
    Encrypted Handshake Message
    RSAParameters Struct
    What if JWT is stolen?
    What's the difference between JWTs and Bearer Token?
    RSA Algorithm Example
    第18届Jolt大奖结果公布
    Ruby on rails开发从头来(windows)(三十六) 调试技巧
    Ruby on rails开发从头来(四十二) ActiveRecord基础(主键和ID)
    YouTube开放基础技术架构 让用户建自家YouTube
    Ruby on rails开发从头来(四十) ActiveRecord基础(Boolean属性)
  • 原文地址:https://www.cnblogs.com/yingkong1987/p/3325972.html
Copyright © 2011-2022 走看看