首页 > 其他 > 详细

转载一篇关于iOS 的CoreData的入门教程

时间:2014-02-25 13:12:51      阅读:470      评论:0      收藏:0      [点我收藏+]

本来打算自己写的,后来发现了如下的文章,就不打算写了。

原文地址:http://www.entlib.net/?p=2470,鉴于排班原因,推荐去原地址进行阅读!

 

由于CoreData涉及的类比较多,先笼统地看一下CoreData的使用思路:

1.通过数据库文件和model映射文件(.xcdatamodeld)创建出Persistent Store Coordinator

2.用Coordinator创建出Managed Object Context

3.通过Context和其他类进行增删改等操作

 

 


 

Core Data是iOS编程中使用持久化数据存储的一种方式。在以前的教程 – 读写应用程序的设置数据,我们已经提供了三种方式来实现App数据的存储,分别为:

(1) 用户默认设置 – 这种情况通常不需要用户干预,如游戏通关信息,Video播放记录,或者App退出之后,下次进入时,希望恢复到退出时的情况。
(2) 设置束(Settings Bundle)– 提供了一个通过iPhone、iPad的设置(Settings)应用程序进行配置的接口。
(3) 直接访问文件系统 – 读写属于当前App的iOS 文件系统部分的文件。

在一些情况下,我们可以考试使用Core Data来持久化数据。需要知道的是,Core Data 并不是数据库本身,是Apple提供的对象持久化技术(Object Persistent Technology)。Core Data 框架为数据变更管理、对象存储、对象读取恢复的功能提供了支持。它可以使用SQLite作为持久化存储的类型,但本身并不是一个数据库。

教程将重点介绍和演示Core Data框架的使用,并不是创建一个完美的应用程序,而是演示基于Core Data框架创建应用程序的过程中,所需要使用到的一些基础类、工具和技术。

下面先了解Core Data框架提供的一些核心类,在App开发过程中需要使用到。

Core Data 堆栈(Stack)

堆栈(Stock)术语用来表述Core Data框架对象集合,这些对象实现从模型对象中获取数据,和保存数据到持久化存储(Persistent Store)中-也就是数据文件。从概念上讲,一个持久化存储(Persistent Store)就像一个数据库,有数据表和数据记录。我们可选用的Core Data存储类型之一是SQLite,但是存储文件并不一定是实际的数据库。

下图是一个简单的、且常用的Core Data堆栈配置。我们通常直接使用的类是堆栈顶部的Managed Object Context(被管理对象上下文)和它所包含的Managed Objects(被管理对象)。

bubuko.com,布布扣

 

被管理对象和被管理对象上下文

被管理对象(Managed Object)是NSManagedObject对象或NSManagedObject子类对象实例。其实,也就是数据库中数据表记录的对象表示,由Core Data管理的模型(Model)对象。被管理对象(Managed Object)表示应用程序中操作的数据,例如人力资源应用系统中的部门和员工;画图应用程序中的各类形状、文本等等。一个被管理对象总是和一个被管理对象上下文(Managed Object Context)相关联。

被管理对象上下文(Managed Object Context)是NSManagedObjectContext对象实例。上下文表示应用程序中的一个单一对象空间,它的主要职责管理被管理对象(Managed Object)集合。这些对象组成了一组关联的模型对象(Model Object),描绘了一个或多个持久化存储(Persistent Store)。上下文(Context)是非常强大的对象,在应用程序中起到中心作用,负责生命周期管理到验证、关系维护(Relationship Maintenance)和撤销/重做特性。

在我们创建一个新的被管理对象时,将它插入到Context中。从数据库中获取的现存记录也将放到Context中,作为被管理对象。所有对被管理对象(Managed Object)的修改都保留在内存中,直到正在提交给持久化存储保存上下文(Context)。

下图描绘了被管理对象上下文(Managed Object Context)包含2个被管理对象(Managed Object),对应着数据库中2条记录。其中一个对象的属性值已经在内存中更新过了,但还没有提交到数据库保存。另外,还有2条记录在数据库中,并没有对应的被管理对象(Managed Object)。

bubuko.com,布布扣

被管理对象模型(Managed Object Model)

被管理对象模型是NSManagedObjectModel实例。被管理对象模型是数据库Schema的对象表示,是实体描述对象(Entity Description Object – NSEntityDescription实例)的集合。

一个实体描述对象(Entity Description Object)描述了数据库中一个数据表实体,包括表名称、表对应的被管理对象的名称、属性(Attributes)和关系(Relationships)等等。

下图描绘了实体描述对象、数据库中数据表和被管理对象(对应数据库表的一条记录)之间的关系。

bubuko.com,布布扣

从上图可以看出,每一个被管理对象(Managed Object)都有对应实体对象的引用。
Core Data使用模型(Model)来映射应用程序中被管理对象和数据库中的数据记录。需要注意的是,如果我们修改数据库的Schema,Core Data将无法使用之前创建的模型来读取数据库记录了。很多持久化机制都存在这个问题。

我们一起看看下面这些示例代码:

Customer *customer1 = (Customer *)[NSEntityDescription insertNewObjectForEntityForName:@"Customer"
inManagedObjectContext:self.managedObjectContext];
customer1.customerID = [NSNumber numberWithInt:1];
customer1.customerName = @"EntLib.com";
customer1.password = @"EntLib.com";
customer1.userName = @"EntLib.com";

Customer 类是NSManagedObject子类,NSEntityDescription insertNewObjectForEntityForName:inManagedObjectContext:方法创建customer1 被管理实体对象,并和被管理对象上下文(Managed Object Context)self.managedObjectContext相关联。

持久化存储协调器(Persistent Store Coordinator)

持久化存储协调器(Persistent Store Coordinator)在Core Data管理数据过程中扮演重要的角色,然而,在使用Core Data框架时,我们通常不必直接和协调器交互。这里我们详细介绍持久化存储协调器,如果你对此没有兴趣,可以跳过去。

持久化存储协调器是NSPersistentStoreCoordinator实例,管理持久化对象存储(Persistent Object Store)集合。一个持久化对象存储表示一个外部存储文件。

下图描绘了协调器扮演的作用。不过,一般iOS 应用程序中,我们通常使用单一存储(也就是一个数据库文件),因此Core Data堆栈并不会像下图这么复杂。

bubuko.com,布布扣

下面一段代码涉及到持久化存储协调器(Persistent Store Coordinator)的应用,创建持久化存储协调器(Persistent Store Coordinator)对象实例,并设置被管理对象上下文(Managed Object Context)的持久化存储协调器(Persistent Store Coordinator)。

NSURL *url = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory
inDomains:NSUserDomainMask] lastObject];
NSURL *storeDatabaseURL = [url URLByAppendingPathComponent:@"ShoppingCart Database"];
// url is now <Document Directory>/ShoppingCart Database
NSError *error = nil;
NSPersistentStoreCoordinator *persistentStoreCoordinator =
[[NSPersistentStoreCoordinator alloc]
initWithManagedObjectModel:[NSManagedObjectModel mergedModelFromBundles:nil]];
if(![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType
configuration:nil
URL:storeDatabaseURL options:nil error:&error])
{
NSLog(@"Error while loading persistent store ...%@", error);
}

 

_managedObjectContext = [[NSManagedObjectContext alloc] init];
[_managedObjectContext setPersistentStoreCoordinator:persistentStoreCoordinator];

 

下面先使用Core Data实现一个简单的范例ShoppingCart。本范例App不利用框架自动生成代码,完全自己编写所有的 Core data 相关代码,来深入理解 Core Data框架中主要类的使用。

使用Xcode的Single View Application模板,创建ShoppingCart项目,类前缀为Cart,如下图所示。

bubuko.com,布布扣

添加CoreData Framework框架

在ShoppingCart项目创建完成之后,我们需要添加CoreData Framework框架到项目中。详细操作步骤,可以参考-开发定位服务的App-教程中,添加Core Location框架的过程。

bubuko.com,布布扣

添加好CoreData Framework之后,将CoreData Framework 拖放到项目的Frameworks目录中,让项目文件看起来更加有序。

创建Data Model(数据模型)

下一步在项目中添加Data Model,选择左侧的Core Data栏位,右侧选择Data Model模板,如下图示:

bubuko.com,布布扣

 

输入名称 CartModel,并点击Create按钮。

bubuko.com,布布扣

项目导航栏中创建了CartModel.xcdatamodeld 文件。
下面,我们在项目中创建Group,命名为Resources,如下图所示。

bubuko.com,布布扣

我们将CartModel文件和Storyboard文件拖放到项目中的Resources 文件夹(或组)中。虽然这一操作不是必须的,但这样操作只是为了让项目文件管理有序。

另外,在项目中再创建一个新组,命名为Model Class,后面会用到。完成之后,项目导航栏如下图所示。

bubuko.com,布布扣

打开CartModel文件,如下图所示:

bubuko.com,布布扣

接着添加Entity(实体)和Attribute(属性),类似于数据表和数据字段。点击上述CartModel界面底部的Add Entity按钮,添加一个新的Entity,命名为Customer。

bubuko.com,布布扣

选择Customer实体,点击Attrubtes下面的+按钮,添加相应的Attribute字段。

bubuko.com,布布扣

逐步添加customerID (Integer 32)、customerName (String)、password (String)和userName (String)属性字段。

重复前面相同的步骤,创建ShoppingCartMaster和ShoppingCartDetail实体,并添加相应的Attribute字段,如下图所示。

bubuko.com,布布扣

这里基于App演示的需要,定义的Entity都比较简单。

下面建立这些Entity之间的关系(Relationship),点击CartModel底部的Editor Style,选择右侧的Graph样式。

bubuko.com,布布扣

显示3个实体及其属性,有点像数据库中的三张表。

[插图省略了]

建立Entity之间的关系有2种方法:(1) 选择ShoppingCartMaster,点击并按住下面的Add Attribute 图标,直到出现弹出菜单,选择Add Relationship选项。

bubuko.com,布布扣

选择新建立的关系,在右侧的Data Model inspector面板窗口,设置Relationship的相关属性,Name设置为 detail,Destination设置为 ShoppingCartDetail,另外选中 To-Many Relationship复选框,因为一个购物车中可能有多个商品。

bubuko.com,布布扣

同时Delete Rule 可以选择Cascade,实现级联删除,表示在使用Core Data删除ShoppingCartMaster中一个对象时,关联的ShoppingCartDetail也将删除。
此时,我们发现Xcode有一个警告信息,如下图所示。

[插图省略了]

ShoppingCart/CartModel.xcdatamodeld/CartModel.xcdatamodel/: warning: Consistency Error: ShoppingCartMaster.detail does not have an inverse; this is an advanced setting (no object can be in multiple destinations for a specific relationship)

意思是说刚刚创建的ShoppingCartMaster.detail关系没有反向关系。Apple建议每一个关系都创建一个反向关系。

好吧!我们在创建一个反向关系。

选择ShoppingCartDetail实体,和之前的操作一样,点击Add Relationship按钮,然后在右侧的Data Model inspector面板窗口,设置Relationship的相关属性,Name设置为 master,Destination设置为 ShoppingCartMaster,Inverse反向关系选择detail。之前创建的detail关系,其反向关系自动设置为master,可以检查一下看看。

[插图省略了]

(2) 另外一种建立Relationship的方法是,在该窗口中,选择实体Customer,按住Control键,并拖拉到shoppingCartMaster实体上。
同样,在Data Model inspector面板窗口中,设置Relationship的相关属性,如下图所示:

[插图省略了]

按照上面的拖拉操作建立Relationship会,一般会自动建立反向的关系,这个比较方便。将Customer实体到ShoppingCartMaster实体的关系命名为cart,将ShoppingCartMaster到Customer的关系命名为customer。

下面是已经建立好Entity关系之后的效果图。

bubuko.com,布布扣

下一步创建需要的NSManagedObjects 子类。

选择CartModel中的三个Entity,接着选择Xcode中的Editor 菜单下面的Generate NSManagedObjects Subclass … 子菜单项。

bubuko.com,布布扣

将生成的子类文件,可选择存放在ShoppingCart项目的Model Class文件夹中。在ShoppingCart项目导航栏中创建了6个文件,分别为Customer.h、Customer.m、ShoppingCartMaster.h、ShoppingCartMaster.m、ShoppingCartDetail.h和ShoppingCartDetail.m。

[插图省略了]

自动生成的这些文件可以让项目启用Core Data框架了,这些自动生成文件不要直接修改。这些自动生成的模型类,都继承NSManagedObject类-被管理对象,也就是数据库中数据表记录的对象表示。

编写Core Data代码

在前面创建Data Model的过程中,我们已经创建3个被管理对象(NSManagedObject),也就是实体对象。

这里,我们创建其他Data Core框架中的对象,启用Core Data数据持久化存储功能。
打开项目中的CartAppDelegate.h文件,添加NSManagedObjectContext属性和2个方法的原型定义。更新后的代码如下所示:

#import <UIKit/UIKit.h>

@interface CartAppDelegate : UIResponder
@property (strong, nonatomic) UIWindow *window;

@property (strong, nonatomic) NSManagedObjectContext *managedObjectContext;
- (void) insertDataIntoShoppingCartDB;
- (void) displayShoppingCart;

@end

打开 CartAppDelegate.m 文件,添加managedObjectContext属性的@synthesize编译指令。
@synthesize managedObjectContext = _managedObjectContext;

进一步实现managedObjectContext 实例变量的初始化方法。这段代码理稍微有点难理解。下面解释一下,首先获取存储文件(或数据库文件)存放位置,这里设置为App的Document目录下,存储文件(或数据库文件)为ShoppingCar Database。
然后,创建NSPersistentStoreCoordinator 对象,指定存储类型(SQLite数据库)和存储路径。最后,实例化被管理对象上下文(Managed Object Context),并赋值其持久化存储协调器。
- (NSManagedObjectContext *) managedObjectContext{
if(_managedObjectContext == nil){
NSURL *url = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory
inDomains:NSUserDomainMask] lastObject];
NSURL *storeDatabaseURL = [url URLByAppendingPathComponent:@"ShoppingCart.sqlite"];
// 设置SQLite 数据库存储路径 /ShoppingCart.sqlite
NSError *error = nil;

//根据被管理对象模型创建NSPersistentStoreCoordinator 对象实例
NSPersistentStoreCoordinator *persistentStoreCoordinator =
[[NSPersistentStoreCoordinator alloc]
initWithManagedObjectModel:[NSManagedObjectModel mergedModelFromBundles:nil]];

//根据指定的存储类型和路径,创建一个新的持久化存储(Persistent Store)
if(![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType
configuration:nil
URL:storeDatabaseURL options:nil error:&error])
{
NSLog(@"Error while loading persistent store ...%@", error);
}

_managedObjectContext = [[NSManagedObjectContext alloc] init];

//设置当前被管理对象上下文的持久化存储协调器
[_managedObjectContext setPersistentStoreCoordinator:persistentStoreCoordinator];
 }

// 返回初始化的被管理对象上下文实例
return _managedObjectContext;
}

 

在CartAppDelegate.m文件实现接口中定义的insertDataIntoShoppingCartDB 方法,该方法主要是箱SQLite数据库中插入一些初始数据,供测试用途。
下面的代码,实现向Customer、ShoppingCartMaster和ShoppingCartDetail三个实体中插入初始化数据。
// 向SQLite插入一些初始数据
- (void) insertDataIntoShoppingCartDB{
// 创建Customer 被管理对象实例
Customer *customer1 = (Customer *)[NSEntityDescription insertNewObjectForEntityForName:@"Customer"
inManagedObjectContext:self.managedObjectContext];
customer1.customerID = [NSNumber numberWithInt:1];
customer1.customerName = @"客户姓名-笨笨";
customer1.password = @"EntLib.com";
customer1.userName = @"EntLib.com";

// 创建ShoppingCartMaster 被管理对象实例
ShoppingCartMaster *shoppingCartMaster1 = (ShoppingCartMaster *)[NSEntityDescription
insertNewObjectForEntityForName:@"ShoppingCartMaster" inManagedObjectContext:self.managedObjectContext];
shoppingCartMaster1.cartID = [NSNumber numberWithInt:100];
shoppingCartMaster1.customerID = [NSNumber numberWithInt:1];

// 创建ShoppingCartDetail 被管理对象实例
ShoppingCartDetail *shoppingCartDetail1= (ShoppingCartDetail *)[NSEntityDescription
insertNewObjectForEntityForName:@"ShoppingCartDetail" inManagedObjectContext:self.managedObjectContext];
shoppingCartDetail1.cartID = [NSNumber numberWithInt:100];
shoppingCartDetail1.currentPrice = [NSDecimalNumber decimalNumberWithDecimal:
[[NSNumber numberWithFloat:18.5f] decimalValue]];

shoppingCartDetail1.productID = @"1-111-111";
shoppingCartDetail1.productName= @"Hello Kitty - iPhone 5 手机";
shoppingCartDetail1.productSysNo = [NSNumber numberWithInt:10];

ShoppingCartDetail *shoppingCartDetail2= (ShoppingCartDetail *)[NSEntityDescription
insertNewObjectForEntityForName:@"ShoppingCartDetail" inManagedObjectContext:self.managedObjectContext];
shoppingCartDetail2.cartID = [NSNumber numberWithInt:100];
shoppingCartDetail2.currentPrice = [NSDecimalNumber decimalNumberWithDecimal:
[[NSNumber numberWithFloat:18.5f] decimalValue]];

shoppingCartDetail2.productID = @"2-222-111";
shoppingCartDetail2.productName= @"Hello Kitty - iPad 3 平板电脑";
shoppingCartDetail2.productSysNo = [NSNumber numberWithInt:10];

// 赋值关系属性实例变量
// 开发测试的时候,可以把下面的代码注释掉,分析比较一下
customer1.cart = shoppingCartMaster1;
[shoppingCartMaster1 addDetailObject:shoppingCartDetail1];
[shoppingCartMaster1 addDetailObject:shoppingCartDetail2];

// 保存数据,持久化存储 SQLite数据库
// 开发测试的时候,可以把下面的代码注释掉,分析比较一下
/*if([self.managedObjectContext hasChanges]){
[self.managedObjectContext save:nil];
}*/

}

注意,上面的代码中,我们把向SQLite数据库写入数据的save操作注释了。这个只是在测试过程中,避免反复向数据库中插入初始数据。如果将上述代码注释去掉,就可以实现向SQLite数据库中存储数据。

继续在CartAppDelegate.m文件中实现接口中定义的displayShoppingCart方法。该方法用来在Xcode的调试窗口输出被管理对象上下文中的实体数据。
// 显示SQLite 数据库,购物车中的内容
- (void) displayShoppingCart{
// NSFetchRequest 对象用来检索数据
NSFetchRequest *request = [[NSFetchRequest alloc] init];

// 根据指定Entity名称和被管理对象上下文,创建NSEntityDescription对象,
NSEntityDescription *myEntityQuery = [NSEntityDescription
entityForName:@"Customer"
inManagedObjectContext:self.managedObjectContext];
// 指定实体
[request setEntity:myEntityQuery];

NSError *error = nil;
// 返回符合查询条件NSFetchRequest的记录数组
NSArray * customerArr = [self.managedObjectContext executeFetchRequest:request error:&error];
NSInteger recordCount = [customerArr count];
NSLog(@"Record Count: %d", recordCount);
for (int i=0; i< recordCount; i++){
// 数组中元素为Customer 被管理对象
Customer *customer = (Customer *)[customerArr objectAtIndex:i];
NSLog(@"CustomerName: %@", customer.customerName);

// 获取Customer 对应关系的ShoppingCartMaster 对象,一对一关系
ShoppingCartMaster *cart = customer.cart;
NSLog(@"我的购物车: %@", cart.cartID);

// 获取ShoppingCartMaster对象中所有的detail 对象数组,一对多关系
NSArray *productList = [cart.detail allObjects];
int numberOfProducts = [productList count];
NSLog(@"购物车商品数:%d", numberOfProducts);

 

// 遍历ShoppingCartDetail 数组
for(int i=0; i< numberOfProducts; i++){
ShoppingCartDetail *detail = [productList objectAtIndex:i];
NSLog(@"购物车商品:%@", detail.productName);
}
}
}

注意,这里我们使用被管理对象上下文(Managed Object Context)中获取数据的,也就是在内存中获取数据,因此前面insertDataIntoShoppingCartDB方法中的[self.managedObjectContext save:nil] 代码行,即使注释,也可以获得返回结果。

最后,我们还需要修改CartAppDelegate.m文件中的application:didFinishLaunchingWithOptions:方法中的代码。更新后的代码如下,主要是添加对我们前面定义的2个方法的调用。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
[self insertDataIntoShoppingCartDB];
[self displayShoppingCart];

 

return YES;
}

 

运行ShoppingCart应用程序

代码编写好了,我们运行App测试一下。

因为UI 视图界面并没有进行任何编码,因此没有什么变化,重点是Xcode调试器控制台的输出内容。NSLog语句的输出就显示在这里的。

bubuko.com,布布扣

这些输出内容,刚好符合我们的预期。Customer 实体中只有一条记录,购物车编号:100,而且购物车添加了2个具体的商品。

检查 SQLite数据库

前面我们说过,SQLite数据库文件存放在App的Document目录下,数据库文件名为ShoppingCart.sqlite,怎么找到它呢?

使用Finder 切换到如下文件夹:

/Users/<您的用户名>/Library/Application Support

/iPhone Simulator/6.0/Applications/<全局唯一的ID>/Documents

注意:目录Library 默认可能是隐藏的,要访问它,可按住Option键,然后单击Finder的前往(Go)菜单,然后选择 – 资源库菜单项,进入Library目录。

另外,其中的5.1 表示的是iOS 模拟器版本号,根据运行App选择的模拟器版本号不同而有变化。

Applications 目录中使用全局唯一的ID,通过查看最新修改的目录列表,可以找到ShoppingCart 应用程序,以及Documents目录的ShoppingCart.sqlite数据库文件。

bubuko.com,布布扣

怎么打开这个文件呢?

我们可以采用SQLite Database Browser工具来打开它。

 

可以从下面这个地址下载这个工具。

http://sourceforge.net/projects/sqlitebrowser/

将前面的ShoppingCart.sqlite数据库文件复制到我们方便访问的一个目录下,然后通过SQLite Database Browser工具打开它。

如果前面insertDataIntoShoppingCartDB方法中的代码行:

[self.managedObjectContext save:nil];

仍然是注释的情况下,数据库中是没有任何记录的。我们可以把该代码行的注释取消掉,然后再次运行App,再次检查一下数据库中的记录。

数据库的结构中,可以看到我们定义的三个实体对象:

bubuko.com,布布扣

默认生成的数据表的名字就是前面创建的Entity的名字,加上字母Z前缀,字段名也是定义的属性命名,然后加上字母Z前缀,而主键是Z_PK,自增长的整型。

查看数据记录:

Customer表中记录信息:

bubuko.com,布布扣

购物车ShoppingCartDetail中商品信息:

bubuko.com,布布扣

查看Execute SQL 选项卡,可以试着写一条SQL 脚本查询一下:

SELECT * FROM ZCustomer c
INNER JOIN ZShoppingCartMaster sm on c.ZCustomerID=sm.ZCustomerID
INNER JOIN ZShoppingCartDetail sd on sm.ZCartID=sd.ZCartID

bubuko.com,布布扣

转载一篇关于iOS 的CoreData的入门教程

原文:http://www.cnblogs.com/breezemist/p/3564854.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!