SlideShare ist ein Scribd-Unternehmen logo
1 von 50
Downloaden Sie, um offline zu lesen
Core Data




范圣刚,princetoad@gmail.com, www.tfan.org
• 保存和加载数据
 • Local or remote?
 • Archiving or Core Data?
• Archiving: 对整个⽂文件进⾏行操作
• Core Data: 操作存储对象的⼦子集
• 性能
对象-关系映射(ORM)
• Core Data 是提供了 object-relational mapping 的⼀一
 个框架,可以把 Objective-C 对象转成存储在
 SQLite 数据库⽂文件的数据,反之亦然。
• Core Data 提供了⼀一种可以提取和存储数据到关系
 数据库⽽而不需要了解 SQL 的能⼒力。
• 我们这⼀一个章节在 Homepwner 的 BNRItemStore
 中⽤用 Core Data 替换掉原来的 keyed archiving。
对 Homepwner 使⽤用Core Data
• 我们现在的 Homepwner 应⽤用使⽤用 archiving 来保
 存和加载数据,对于数据量较⼩小的情况还可以,
 但是对于数据量⾮非常⼤大的情况下,我们可能就想
 要能够增量提取和更新数据的 Core Data。
• ⾸首先,我们还是要把 Core Data framework 加⼊入我
 们的项⺫⽬目。

• 选择 Homepwner target,在 Build Phases 下⾯面,
 打开 Link Binary with Libraries, 加 + 号 添加 Core
 Data framework
模型⽂文件
Core Data ⾓角⾊色
• table/class -> entity
• columns/instance -> attributes
• BNRItem entity
• 打开 Homepwner.xcodeproj
• File -> New -> File, iOS -> Core Data, Data Model, 命
 名为 Homepwner

• 将会创建⼀一个 Homepwner.xcdatamodeld 的⽂文件
• 从 project navigator 中打开这个⽂文件,我们就可以
 看到可以操作 Core Data model file 的⽤用户界⾯面
• 找到屏幕左下⾓角的 “Add Entity”按钮点击,⼀一个新
 的 Entity 将会出现在左⼿手边的 entities 列表中,命名
 为 BNRItem
在 Attributes 中对应的设置属性


    Attribute          type
     itemName          String

    serialNumber       String

    valueInDollars   Integer 32

    dateCreated         Date

      imageKey         String

     thumbnail       Binary Data

     thumbnail       Undefined
• 从 Attributes 中选择 thumbnail,点击 inspector 中
 的 attribute inspector,勾选 Transient

• 设成 Transient 是告诉 Core Data 我们的 thumbnail
 将在运⾏行时创建,⽽而不是从⽂文件保存和加载。
再增加⼀一个⽤用于排序的属性
• orderingValue -> Double
relationship
• ⺫⽬目前模型⽂文件对于保存和加载 items 已经⾜足够了
• 我们再增加⼀一个新的名为 BNRAssetType 的实体,
 ⽤用于描述 items 的分类。
• 这样就构造出⼀一个实体间的 relation,演⽰示 Core
 Data 实体间关系的功能。
• 再添加⼀一个名为 BNRAssetType 的实体⽂文件
• 增加⼀一个叫做 label 的属性,类型是 String,把它
 作为 items 分类的名字
• 现在我们需要来建⽴立 BNRAssetType 和 BNRItem 之
 间的关系
Homepwner 中的实体
• 给模型⽂文件增加 relationships。
• 选择 BNRAssetType 实体,点击 Relationship 部分
 的 + 号。
 • 在 Relationship 列把这个 relation 命名为 items;
 • 然后从 Destination 列中选择 BNRItem;
 • 在 data model inspector 中,勾选 To-Many Relationship
• 给 BNRItem 实体增加⼀一个名为 assetType 的关系,
 把 BNRAssetType 作为⺫⽬目的,在 Inverse 列,选择
 items。
NSManagedObject 及其⼦子类
• 当⼀一个对象被提取出来时,默认类型是
NSManagedObject,是 NSObject的⼀一个⼦子类,知
道如何跟 Core Data 互相操作。

• NSManagedObject 类似字典:持有实体中所有属
性的⼀一个 key-value pair。

• NSManagedObject 差不多就是⼀一个容器。如果我
们想让模型对象做更多⼯工作,我们必须⼦子类化
NSManagedObject。
• 选中 BNRItem 实体,显⽰示 data model inspector,
 并更改 Class 字段为 BNRItem。

• 现在当 BNRItem 实体被提取出来时,这个对象的
 类型将会是 BNRItem。
• 在 Finder 中,⾸首先备份好我们的 BNRItem.h 和
 BNRItem.m ⽂文件

• 然后在 Xcode 中把这两个⽂文件从 project navigator
 中删除。
• 重新打开 Homepwner.xcdatamodeld
• 选择 BNRItem 实体
• 然后选择从 New 菜单中选择 File,iOS -> Core
 Data, 选择 NSManagedObject subclass 选项,提⽰示
 保存的时候,勾选“Use scalar properties for
 primitive data types”
• Xcode 将会⽣生成两个新的 BNRItem.h 和 BNRItem.m
   ⽂文件

 • 打开 BNRItem.h, 把 thumbnail 属性的类型改成
   UIImage

 • 然后增加⼀一个跟之前⼀一样的⼀一个⽅方法声明
@interface BNRItem : NSManagedObject

@property (nonatomic) int32_t itemName;
@property (nonatomic, retain) NSString * serialNumber;
@property (nonatomic) int32_t valueInDollars;
@property (nonatomic) NSTimeInterval dateCreated;
@property (nonatomic, retain) NSString * imageKey;
@property (nonatomic, retain) NSData * thumbnailData;
//@property UNKNOWN_TYPE UNKNOWN_TYPE thumbnail;
@property (nonatomic, strong)UIImage *thumbnail;
@property (nonatomic) double orderingValue;
@property (nonatomic, retain) NSManagedObject *assetType;

- (void)setThumbnailDataFromImage:(UIImage *)image;

@end
• NSDate 变成了 NSTimeInterval
 • 打开我们的 DetailViewController.h, 定位到
     viewWillAppear:, 替换下⾯面的代码

//    [dateLabel setText:[dateFormatter stringFromDate:[item dateCreated]]];
    // 把 time interval 转换成 NSDate
    NSDate *date = [NSDate dateWithTimeIntervalSinceReferenceDate:[item
dateCreated]];
    [dateLabel setText:[dateFormatter stringFromDate:date]];
• 然后,从旧的 BNRItem.m 中拷⻉贝
      setThumbNailFromImage: ⽅方法到新的⽂文件
- (void)setThumbnailDataFromImage:(UIImage *)image
{
    CGSize origImageSize = [image size];

    // thumnail 的矩形⼤大⼩小
    CGRect newRect = CGRectMake(0, 0, 40, 40);
    // 计算缩放⽐比
    float ratio = MAX(newRect.size.width / origImageSize.width, newRect.size.height / origImageSize.height);

    // ⽣生成⼀一个带缩放因⼦子的透明位图上下⽂文
    UIGraphicsBeginImageContextWithOptions(newRect.size, NO, 0.0);

    // ⽣生成⼀一个圆⾓角矩形路径
    UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:newRect cornerRadius:5.0];
    // 后续绘制 clip 到这个圆⾓角矩形
    [path addClip];

    // 图⽚片放到缩略图中间
    CGRect projectRect;
    projectRect.size.width = ratio * origImageSize.width;
    projectRect.size.height = ratio * origImageSize.height;
    projectRect.origin.x = (newRect.size.width - projectRect.size.width) / 2.0;
    projectRect.origin.y = (newRect.size.height - projectRect.size.height) / 2.0;

    // 把图⽚片绘制上来
    [image drawInRect:projectRect];

    // 从图⽚片上下⽂文获得图⽚片,作为我们的缩略图保存
    UIImage *smallImage = UIGraphicsGetImageFromCurrentImageContext();
    [self setThumbnail:smallImage];

    // 得到该图⽚片 PNG 形式,把它作为我们可以 archive 的数据
    NSData *data = UIImagePNGRepresentation(smallImage);
    [self setThumbnailData:data];

    // 完成以后,清除图⽚片上下⽂文资源
    UIGraphicsEndImageContext();
}
• 在 BNRItem.m 中重写 awakeFromFetch ⽅方法,来
     从 thumbnailData 设置 thumbnail

    • 使⽤用 archiving 的时候我们是在 initWithCoder: 时
     做的
- (void)awakeFromFetch
{
    [super awakeFromFetch];

     UIImage *tn = [UIImage imageWithData:[self thumbnailData]];
     [self setPrimitiveValue:tn forKey:@"thumbnail"];
}
• 我们创建⼀一个新的 BNRItem 实例时,它将会被加
    ⼊入数据库。

 • 当对象被添加到数据库时,它将被发送
    awakeInsert 消息

 • 在 BNRItem.m 中实现 awakeFromInsert
 • 原先我们是在 BNRItem 的 designated initializer 中
    添加⼀一些附加⾏行为的
- (void)awakeFromInsert
{
    [super awakeFromInsert];
    NSTimeInterval t = [[NSDate date] timeIntervalSinceReferenceDate];
    [self setDateCreated:t];
}
更新 BNRItemStore
BNRItemStore 和
NSManagedObjectContext
• 在 BNRItemStore.h 中,导⼊入 Core Data 然后增加三
    个实例变量

#import <Foundation/Foundation.h>
// 导⼊入 Core Data 头⽂文件
#import <CoreData/CoreData.h>

@class BNRItem;

@interface BNRItemStore : NSObject
{
    NSMutableArray *allItems;
    NSMutableArray *allAssetTypes;
    NSManagedObjectContext *context;
    NSManagedObjectModel *model;
}
• 在BNRItemStore.m 中,更改 itemArchivePath 的实
    现来返回⼀一个不同的路径供 Core Data 存储数据

- (NSString *)itemArchivePath
{
    NSArray *documentDirectories =
NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask,
YES);
    NSString *documentDirectory = [documentDirectories objectAtIndex:0];

//    return [documentDirectory
stringByAppendingPathComponent:@"items.archive"];
    return [documentDirectory stringByAppendingPathComponent:@"store.data"];
}
• BNRItemStore 被初始化时,它需要设置
 NSManagedObjectContext 和
 NSPersistentStoreCoordinator

• 我们需要创建⼀一个 NSManagedObjectModel 来持
 有 Homepwner.xcdatamodeld 的实体信息,并且
 使⽤用这个对象初始化 persistent store coordinator

• 因此我们将创建 NSManagedObjectContext 的实
 例,并且指定它使⽤用这个 persistent store
 coordinator 来保存和加载对象
• 在 BNRItemStore.m 中更新 init
- (id)init
{
    self = [super init];
    if (self) {
////         allItems = [[NSMutableArray alloc] init];
//         NSString *path = [self itemArchivePath];
//         allItems = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
//         // 如果数组之前没有被保存,创建⼀一个新的空数组
//         if (!allItems) {
//             allItems = [[NSMutableArray alloc] init];
//         }
        // 读取 Homepwner.xcdatamodeld
        model = [NSManagedObjectModel mergedModelFromBundles:nil];

        NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc]
initWithManagedObjectModel:model];

        // SQLite ⽂文件在哪⼉儿?
        NSString *path = [self itemArchivePath];
        NSURL *storeURL = [NSURL fileURLWithPath:path];

        NSError *error = nil;

        if (![psc addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil
error:&error]) {
            [NSException raise:@"Open failed" format:@"Reason: %@", [error localizedDescription]];
        }

        // ⽣生成 managed object context
        context = [[NSManagedObjectContext alloc] init];
        [context setPersistentStoreCoordinator:psc];

        // 不需要管理 undo
        [context setUndoManager:nil];
    }

    return self;
}
• 之前我们使⽤用 keyed archiving 的时候,当我们要
   求保存数据,BNRItemStore 将会写⼊入整个
   NSMutableArray 的 BNRItems。

• 现在我们让它给 NSManagedObjectContext 发送
   save: 消息。Context 将会更新 store.data 中从最后
   ⼀一次保存其有任何变更的所有记录。

• 在 BNRItemStore.m 中,更改 saveChanges
@implementation BNRItemStore
- (BOOL)saveChanges
{
//    // 返回成功或失败
//    NSString *path = [self itemArchivePath];
//    return [NSKeyedArchiver archiveRootObject:allItems toFile:path];
    NSError *err = nil;
    BOOL successful = [context save:&err];
    if (!successful) {
        NSLog(@"Error saving: %@", [err localizedDescription]);
    }
    return successful;
}
NSFetchRequest 和 NSPredicate
• 要从 NSManagedObjectContext 提取数据,我们需
 要 prepare and execute ⼀一个 NSFetchRequest。

• 当这个 fetch 请求被执⾏行以后,我们将得到⼀一个符
 合这个请求的 parameters 的所有对象的⼀一个数组

• fetch 请求需要我们想要从中获取对象的⼀一个实体
 的描述。这⾥里我们指定 BNRItem 实体。
• 也可以设置请求的 sort descriptors 来指定数组中
   对象的顺序。

 • ⼀一个 sort descriptor 具有⼀一个映射到⼀一个 attribute
   的 key,和⼀一个表⽰示正序还是倒序的 BOOL 值

 • 我们希望使⽤用使⽤用 orderingValue 正序排列返回的
   BNRItem

 • 在 BNRItemStore.h 中,声明⼀一个新的⽅方法
- (void)loadAllItems;
• 在 BNRItemStore.m 中定义 loadAllItems 来 prepare
     and execute ⼀一个 fetch 请求,并且保存结果到
     allItems 数组
- (void)loadAllItems
{
    if (!allItems) {
        NSFetchRequest *request = [[NSFetchRequest alloc] init];
        NSEntityDescription *e = [[model entitiesByName]
objectForKey:@"BNRItem"];
        [request setEntity:e];

        NSSortDescriptor *sd = [NSSortDescriptor
sortDescriptorWithKey:@"orderingValue" ascending:YES];
        [request setSortDescriptors:[NSArray arrayWithObject:sd]];

        NSError *error;
        NSArray *result = [context executeFetchRequest:request error:&error];
        if (!result) {
            [NSException raise:@"Fetch failed" format:@"Reason: %@", [error
localizedDescription]];
        }

         allItems = [[NSMutableArray alloc] initWithArray:result];
     }
}
• 在 BNRItemStore.m 中,在 init 的末尾发送这个消
     息给 BNRItemStore


         // 不需要管理 undo
         [context setUndoManager:nil];

         [self loadAllItems];
     }

     return self;
}
• 要想有选择性的 fetch ⼀一些实例,我们可以添加⼀一
 个 predicate(⼀一个 NSPredicate)到我们的 fetch 请
 求,只有满⾜足这个 predicate 的对象会被返回。

• predicate 包含的是⼀一个可以为 true 或 false 的条
 件。例如:
  // ⼀一个 predicate 的例⼦子,设定 fetch 条件
  NSPredicate *p = [NSPredicate predicateWithFormat:@"valueInDollars > 50"];
  [request setPredicate:p];




• predicate 也可以被⽤用来过滤⼀一个数组的内容,例如
     // predicate ⽤用来过滤数组的例⼦子
     NSArray *expensiveStuff = [allItems filteredArrayUsingPredicate:p];
增加和删除 items
• 要创建⼀一个新的 BNRItem,我们将请求
     NSManagedObjectContex 从 BNRItem 实体插⼊入⼀一
     个新的对象。它将返回⼀一个 BNRItem 的实例。

    • 在 BNRItemStore.m 中,编辑 createItem ⽅方法
- (BNRItem *)createItem
{
////     BNRItem *p = [BNRItem randomItem];
//     BNRItem *p = [[BNRItem alloc] init];
    double order;
    if ([allItems count] == 0) {
         order = 1.0;
     } else {
         order = [[allItems lastObject] orderingValue] + 1.0;
     }
    NSLog(@"在 %d 个 items 之后添加,顺序是:%.2f", [allItems count], order);

    BNRItem *p = [NSEntityDescription
insertNewObjectForEntityForName:@"BNRItem" inManagedObjectContext:context];
    [p setOrderingValue:order];

     [allItems addObject:p];
     return p;
}
• 当⼀一个⽤用户删除了⼀一个 item 的时候,我们必须通
     知 context,这样它也会被从数据库删除。

    • 在 BNRItem.m 中,增加下列的代码到 removeItem:
- (void)removeItem:(BNRItem *)p
{
    // BNRItem 被从 store 中移除的时候,它的 image 也应该被从⽂文件系统删除
    NSString *key = [p imageKey];
    [[BNRImageStore sharedStore] deleteImageForkey:key];

     // 增加通知 context 有数据被删除的代码
     [context deleteObject:p];

     [allItems removeObjectIdenticalTo:p];
}
重排 items
• 我们需要为 BNRItem 替换的最后⼀一点功能是在
 BNRItemStore 中重新排序 BNRItems。

• 因为 Core Data 不会⾃自动化的处理排序,每次它在
 table view 中被移动时我们必须更新 BNRItem 的
 orderingValue。

• 在 BNRItemStore.m 中,修改
 moveItemAtIndex:toIndex: 来处理重新排序 items
- (void)moveItemAtIndex:(int)from toIndex:(int)to
{
    if (from == to) {
        return;
    }
    // 得到被移动的对象的指针,以便我们可以把它重新插⼊入
    BNRItem *p = [allItems objectAtIndex:from];

    // 从数组中删除
    [allItems removeObjectAtIndex:from];
    // 在新的位置重新插⼊入
    [allItems insertObject:p atIndex:to];

    // Core Data 的排序
    // 为被移动的对象计算⼀一个新的 orderValue
    double lowerBound = 0.0;

    // 数组中在它之前是否有⼀一个对象?
    if (to > 0) {
        lowerBound = [[allItems objectAtIndex:to -1] orderingValue];
    } else {
        lowerBound = [[allItems objectAtIndex:1] orderingValue] - 2.0;
    }

    double upperBound = 0.0;

    // 数组中在它之后是否有⼀一个对象
    if (to < [allItems count] - 1) {
        upperBound = [[allItems objectAtIndex:to + 1] orderingValue];
    } else {
        upperBound = [[allItems objectAtIndex:to -1] orderingValue] + 2.0;
    }

    double newOrderValue = (lowerBound + upperBound) / 2.0;

    NSLog(@"moving to order: %f", newOrderValue);
}

Weitere ähnliche Inhalte

Was ist angesagt?

Ch07 使用 JSTL
Ch07 使用 JSTLCh07 使用 JSTL
Ch07 使用 JSTLJustin Lin
 
CH09:Collection與Map
CH09:Collection與MapCH09:Collection與Map
CH09:Collection與MapJustin Lin
 
10. 資料永續與交換
10. 資料永續與交換10. 資料永續與交換
10. 資料永續與交換Justin Lin
 
Template mb-kao
Template mb-kaoTemplate mb-kao
Template mb-kaoxwcoder
 
Backbone js and requirejs
Backbone js and requirejsBackbone js and requirejs
Backbone js and requirejsChi-wen Sun
 
合久必分,分久必合
合久必分,分久必合合久必分,分久必合
合久必分,分久必合Qiangning Hong
 
Servlet & JSP 教學手冊第二版 - 課後練習解答
Servlet & JSP 教學手冊第二版 - 課後練習解答Servlet & JSP 教學手冊第二版 - 課後練習解答
Servlet & JSP 教學手冊第二版 - 課後練習解答Justin Lin
 
Introduction and Internals of SQL on hadoop by WangHaihua
Introduction and Internals of SQL on hadoop by WangHaihuaIntroduction and Internals of SQL on hadoop by WangHaihua
Introduction and Internals of SQL on hadoop by WangHaihuaWang Haihua
 
NoSQL-MongoDB介紹
NoSQL-MongoDB介紹NoSQL-MongoDB介紹
NoSQL-MongoDB介紹國昭 張
 
Ch05 Servlet 進階 API、過濾器與傾聽器
Ch05 Servlet 進階 API、過濾器與傾聽器Ch05 Servlet 進階 API、過濾器與傾聽器
Ch05 Servlet 進階 API、過濾器與傾聽器Justin Lin
 
Script with engine
Script with engineScript with engine
Script with engineWebrebuild
 
Static server介绍
Static server介绍Static server介绍
Static server介绍sun jamie
 
CH16:整合資料庫
CH16:整合資料庫CH16:整合資料庫
CH16:整合資料庫Justin Lin
 
Docker進階探討
Docker進階探討Docker進階探討
Docker進階探討國昭 張
 
Parse, cloud code 介紹
Parse, cloud code 介紹Parse, cloud code 介紹
Parse, cloud code 介紹wantingj
 
Servlet & JSP 教學手冊第二版 - 第 1 章:簡介Web應用程式
Servlet & JSP 教學手冊第二版 - 第 1 章:簡介Web應用程式Servlet & JSP 教學手冊第二版 - 第 1 章:簡介Web應用程式
Servlet & JSP 教學手冊第二版 - 第 1 章:簡介Web應用程式Justin Lin
 
Hibernate查询
Hibernate查询Hibernate查询
Hibernate查询llying
 
Ch03 請求與回應
Ch03 請求與回應Ch03 請求與回應
Ch03 請求與回應Justin Lin
 
Servlet & JSP 教學手冊第二版 - 第 3 章:請求與回應
Servlet & JSP 教學手冊第二版 - 第 3 章:請求與回應Servlet & JSP 教學手冊第二版 - 第 3 章:請求與回應
Servlet & JSP 教學手冊第二版 - 第 3 章:請求與回應Justin Lin
 

Was ist angesagt? (20)

Ch07 使用 JSTL
Ch07 使用 JSTLCh07 使用 JSTL
Ch07 使用 JSTL
 
CH09:Collection與Map
CH09:Collection與MapCH09:Collection與Map
CH09:Collection與Map
 
10. 資料永續與交換
10. 資料永續與交換10. 資料永續與交換
10. 資料永續與交換
 
Template mb-kao
Template mb-kaoTemplate mb-kao
Template mb-kao
 
Backbone js and requirejs
Backbone js and requirejsBackbone js and requirejs
Backbone js and requirejs
 
合久必分,分久必合
合久必分,分久必合合久必分,分久必合
合久必分,分久必合
 
Servlet & JSP 教學手冊第二版 - 課後練習解答
Servlet & JSP 教學手冊第二版 - 課後練習解答Servlet & JSP 教學手冊第二版 - 課後練習解答
Servlet & JSP 教學手冊第二版 - 課後練習解答
 
Introduction and Internals of SQL on hadoop by WangHaihua
Introduction and Internals of SQL on hadoop by WangHaihuaIntroduction and Internals of SQL on hadoop by WangHaihua
Introduction and Internals of SQL on hadoop by WangHaihua
 
Node way
Node wayNode way
Node way
 
NoSQL-MongoDB介紹
NoSQL-MongoDB介紹NoSQL-MongoDB介紹
NoSQL-MongoDB介紹
 
Ch05 Servlet 進階 API、過濾器與傾聽器
Ch05 Servlet 進階 API、過濾器與傾聽器Ch05 Servlet 進階 API、過濾器與傾聽器
Ch05 Servlet 進階 API、過濾器與傾聽器
 
Script with engine
Script with engineScript with engine
Script with engine
 
Static server介绍
Static server介绍Static server介绍
Static server介绍
 
CH16:整合資料庫
CH16:整合資料庫CH16:整合資料庫
CH16:整合資料庫
 
Docker進階探討
Docker進階探討Docker進階探討
Docker進階探討
 
Parse, cloud code 介紹
Parse, cloud code 介紹Parse, cloud code 介紹
Parse, cloud code 介紹
 
Servlet & JSP 教學手冊第二版 - 第 1 章:簡介Web應用程式
Servlet & JSP 教學手冊第二版 - 第 1 章:簡介Web應用程式Servlet & JSP 教學手冊第二版 - 第 1 章:簡介Web應用程式
Servlet & JSP 教學手冊第二版 - 第 1 章:簡介Web應用程式
 
Hibernate查询
Hibernate查询Hibernate查询
Hibernate查询
 
Ch03 請求與回應
Ch03 請求與回應Ch03 請求與回應
Ch03 請求與回應
 
Servlet & JSP 教學手冊第二版 - 第 3 章:請求與回應
Servlet & JSP 教學手冊第二版 - 第 3 章:請求與回應Servlet & JSP 教學手冊第二版 - 第 3 章:請求與回應
Servlet & JSP 教學手冊第二版 - 第 3 章:請求與回應
 

Ähnlich wie 16 CoreData

iOS程序设计-数据持久化
iOS程序设计-数据持久化iOS程序设计-数据持久化
iOS程序设计-数据持久化qiyutan
 
15 Subclassing UITableViewCell
15 Subclassing UITableViewCell15 Subclassing UITableViewCell
15 Subclassing UITableViewCellTom Fan
 
09 UITableView and UITableViewController
09 UITableView and UITableViewController09 UITableView and UITableViewController
09 UITableView and UITableViewControllerTom Fan
 
Migrations 與 Schema操作
Migrations 與 Schema操作Migrations 與 Schema操作
Migrations 與 Schema操作Shengyou Fan
 
10 Editing UITableView
10 Editing UITableView10 Editing UITableView
10 Editing UITableViewTom Fan
 
Chapter 4 models
Chapter 4 modelsChapter 4 models
Chapter 4 modelsEkman Hsieh
 
Java华为面试题
Java华为面试题Java华为面试题
Java华为面试题yiditushe
 
Spring4.x + hibernate4.x_配置详解
Spring4.x + hibernate4.x_配置详解Spring4.x + hibernate4.x_配置详解
Spring4.x + hibernate4.x_配置详解zany_hui
 
Django development
Django developmentDjango development
Django developmentloveyudu
 
Servlet & JSP 教學手冊第二版 - 第 7 章:使用 JSTL
Servlet & JSP 教學手冊第二版 - 第 7 章:使用 JSTLServlet & JSP 教學手冊第二版 - 第 7 章:使用 JSTL
Servlet & JSP 教學手冊第二版 - 第 7 章:使用 JSTLJustin Lin
 
iPhone,ios,Object-c基础入门
iPhone,ios,Object-c基础入门iPhone,ios,Object-c基础入门
iPhone,ios,Object-c基础入门Lucien Li
 
iPhone,ios,Object-C基础入门
iPhone,ios,Object-C基础入门iPhone,ios,Object-C基础入门
iPhone,ios,Object-C基础入门Lucien Li
 
Android resource-management
Android resource-managementAndroid resource-management
Android resource-managementLucas Xu
 
從模組到類別
從模組到類別從模組到類別
從模組到類別Justin Lin
 
Nosql及其主要产品简介
Nosql及其主要产品简介Nosql及其主要产品简介
Nosql及其主要产品简介振林 谭
 
掌星 移动互联网开发笔记-Vol001
掌星 移动互联网开发笔记-Vol001掌星 移动互联网开发笔记-Vol001
掌星 移动互联网开发笔记-Vol001rainx1982
 

Ähnlich wie 16 CoreData (20)

iOS程序设计-数据持久化
iOS程序设计-数据持久化iOS程序设计-数据持久化
iOS程序设计-数据持久化
 
15 Subclassing UITableViewCell
15 Subclassing UITableViewCell15 Subclassing UITableViewCell
15 Subclassing UITableViewCell
 
09 UITableView and UITableViewController
09 UITableView and UITableViewController09 UITableView and UITableViewController
09 UITableView and UITableViewController
 
Html 5 native drag
Html 5 native dragHtml 5 native drag
Html 5 native drag
 
Migrations 與 Schema操作
Migrations 與 Schema操作Migrations 與 Schema操作
Migrations 與 Schema操作
 
10 Editing UITableView
10 Editing UITableView10 Editing UITableView
10 Editing UITableView
 
Javascript
JavascriptJavascript
Javascript
 
Chapter 4 models
Chapter 4 modelsChapter 4 models
Chapter 4 models
 
Java华为面试题
Java华为面试题Java华为面试题
Java华为面试题
 
Spring4.x + hibernate4.x_配置详解
Spring4.x + hibernate4.x_配置详解Spring4.x + hibernate4.x_配置详解
Spring4.x + hibernate4.x_配置详解
 
Django development
Django developmentDjango development
Django development
 
Servlet & JSP 教學手冊第二版 - 第 7 章:使用 JSTL
Servlet & JSP 教學手冊第二版 - 第 7 章:使用 JSTLServlet & JSP 教學手冊第二版 - 第 7 章:使用 JSTL
Servlet & JSP 教學手冊第二版 - 第 7 章:使用 JSTL
 
iPhone,ios,Object-c基础入门
iPhone,ios,Object-c基础入门iPhone,ios,Object-c基础入门
iPhone,ios,Object-c基础入门
 
iPhone,ios,Object-C基础入门
iPhone,ios,Object-C基础入门iPhone,ios,Object-C基础入门
iPhone,ios,Object-C基础入门
 
ios分享
ios分享ios分享
ios分享
 
I os 02
I os 02I os 02
I os 02
 
Android resource-management
Android resource-managementAndroid resource-management
Android resource-management
 
從模組到類別
從模組到類別從模組到類別
從模組到類別
 
Nosql及其主要产品简介
Nosql及其主要产品简介Nosql及其主要产品简介
Nosql及其主要产品简介
 
掌星 移动互联网开发笔记-Vol001
掌星 移动互联网开发笔记-Vol001掌星 移动互联网开发笔记-Vol001
掌星 移动互联网开发笔记-Vol001
 

Mehr von Tom Fan

PhoneGap 通信原理和插件系统
PhoneGap 通信原理和插件系统PhoneGap 通信原理和插件系统
PhoneGap 通信原理和插件系统Tom Fan
 
HTML5 Web workers
HTML5 Web workersHTML5 Web workers
HTML5 Web workersTom Fan
 
Web sockets
Web socketsWeb sockets
Web socketsTom Fan
 
Semantics
SemanticsSemantics
SemanticsTom Fan
 
Multimedia
MultimediaMultimedia
MultimediaTom Fan
 
Intro to-html5
Intro to-html5Intro to-html5
Intro to-html5Tom Fan
 
Html5 history
Html5 historyHtml5 history
Html5 historyTom Fan
 
Geolocation
GeolocationGeolocation
GeolocationTom Fan
 
File api
File apiFile api
File apiTom Fan
 
Deviceaccess
DeviceaccessDeviceaccess
DeviceaccessTom Fan
 
Webstorage
WebstorageWebstorage
WebstorageTom Fan
 
Html5 最重要的部分
Html5 最重要的部分Html5 最重要的部分
Html5 最重要的部分Tom Fan
 
AT&T 的 HTML5 策略和应用现状
AT&T 的 HTML5 策略和应用现状AT&T 的 HTML5 策略和应用现状
AT&T 的 HTML5 策略和应用现状Tom Fan
 
PhoneGap 2.0 开发
PhoneGap 2.0 开发PhoneGap 2.0 开发
PhoneGap 2.0 开发Tom Fan
 
Android 平台 HTML5 应用开发
Android 平台 HTML5 应用开发Android 平台 HTML5 应用开发
Android 平台 HTML5 应用开发Tom Fan
 
HTML5 生态系统和应用架构模型
HTML5 生态系统和应用架构模型HTML5 生态系统和应用架构模型
HTML5 生态系统和应用架构模型Tom Fan
 
18 NSUserDefaults
18 NSUserDefaults18 NSUserDefaults
18 NSUserDefaultsTom Fan
 
13 UIPopoverController and Modal View Controller
13 UIPopoverController and Modal View Controller13 UIPopoverController and Modal View Controller
13 UIPopoverController and Modal View ControllerTom Fan
 

Mehr von Tom Fan (20)

PhoneGap 通信原理和插件系统
PhoneGap 通信原理和插件系统PhoneGap 通信原理和插件系统
PhoneGap 通信原理和插件系统
 
HTML5 Web workers
HTML5 Web workersHTML5 Web workers
HTML5 Web workers
 
Web sockets
Web socketsWeb sockets
Web sockets
 
Storage
StorageStorage
Storage
 
Semantics
SemanticsSemantics
Semantics
 
Multimedia
MultimediaMultimedia
Multimedia
 
Intro to-html5
Intro to-html5Intro to-html5
Intro to-html5
 
Html5 history
Html5 historyHtml5 history
Html5 history
 
Geolocation
GeolocationGeolocation
Geolocation
 
File api
File apiFile api
File api
 
Deviceaccess
DeviceaccessDeviceaccess
Deviceaccess
 
Css3
Css3Css3
Css3
 
Webstorage
WebstorageWebstorage
Webstorage
 
Html5 最重要的部分
Html5 最重要的部分Html5 最重要的部分
Html5 最重要的部分
 
AT&T 的 HTML5 策略和应用现状
AT&T 的 HTML5 策略和应用现状AT&T 的 HTML5 策略和应用现状
AT&T 的 HTML5 策略和应用现状
 
PhoneGap 2.0 开发
PhoneGap 2.0 开发PhoneGap 2.0 开发
PhoneGap 2.0 开发
 
Android 平台 HTML5 应用开发
Android 平台 HTML5 应用开发Android 平台 HTML5 应用开发
Android 平台 HTML5 应用开发
 
HTML5 生态系统和应用架构模型
HTML5 生态系统和应用架构模型HTML5 生态系统和应用架构模型
HTML5 生态系统和应用架构模型
 
18 NSUserDefaults
18 NSUserDefaults18 NSUserDefaults
18 NSUserDefaults
 
13 UIPopoverController and Modal View Controller
13 UIPopoverController and Modal View Controller13 UIPopoverController and Modal View Controller
13 UIPopoverController and Modal View Controller
 

16 CoreData

  • 2. • 保存和加载数据 • Local or remote? • Archiving or Core Data? • Archiving: 对整个⽂文件进⾏行操作 • Core Data: 操作存储对象的⼦子集 • 性能
  • 3. 对象-关系映射(ORM) • Core Data 是提供了 object-relational mapping 的⼀一 个框架,可以把 Objective-C 对象转成存储在 SQLite 数据库⽂文件的数据,反之亦然。 • Core Data 提供了⼀一种可以提取和存储数据到关系 数据库⽽而不需要了解 SQL 的能⼒力。 • 我们这⼀一个章节在 Homepwner 的 BNRItemStore 中⽤用 Core Data 替换掉原来的 keyed archiving。
  • 5. • 我们现在的 Homepwner 应⽤用使⽤用 archiving 来保 存和加载数据,对于数据量较⼩小的情况还可以, 但是对于数据量⾮非常⼤大的情况下,我们可能就想 要能够增量提取和更新数据的 Core Data。
  • 6. • ⾸首先,我们还是要把 Core Data framework 加⼊入我 们的项⺫⽬目。 • 选择 Homepwner target,在 Build Phases 下⾯面, 打开 Link Binary with Libraries, 加 + 号 添加 Core Data framework
  • 9. • table/class -> entity • columns/instance -> attributes • BNRItem entity
  • 10. • 打开 Homepwner.xcodeproj • File -> New -> File, iOS -> Core Data, Data Model, 命 名为 Homepwner • 将会创建⼀一个 Homepwner.xcdatamodeld 的⽂文件
  • 11. • 从 project navigator 中打开这个⽂文件,我们就可以 看到可以操作 Core Data model file 的⽤用户界⾯面
  • 12. • 找到屏幕左下⾓角的 “Add Entity”按钮点击,⼀一个新 的 Entity 将会出现在左⼿手边的 entities 列表中,命名 为 BNRItem
  • 13. 在 Attributes 中对应的设置属性 Attribute type itemName String serialNumber String valueInDollars Integer 32 dateCreated Date imageKey String thumbnail Binary Data thumbnail Undefined
  • 14. • 从 Attributes 中选择 thumbnail,点击 inspector 中 的 attribute inspector,勾选 Transient • 设成 Transient 是告诉 Core Data 我们的 thumbnail 将在运⾏行时创建,⽽而不是从⽂文件保存和加载。
  • 16. relationship • ⺫⽬目前模型⽂文件对于保存和加载 items 已经⾜足够了 • 我们再增加⼀一个新的名为 BNRAssetType 的实体, ⽤用于描述 items 的分类。 • 这样就构造出⼀一个实体间的 relation,演⽰示 Core Data 实体间关系的功能。
  • 17. • 再添加⼀一个名为 BNRAssetType 的实体⽂文件 • 增加⼀一个叫做 label 的属性,类型是 String,把它 作为 items 分类的名字
  • 18. • 现在我们需要来建⽴立 BNRAssetType 和 BNRItem 之 间的关系
  • 20. • 给模型⽂文件增加 relationships。 • 选择 BNRAssetType 实体,点击 Relationship 部分 的 + 号。 • 在 Relationship 列把这个 relation 命名为 items; • 然后从 Destination 列中选择 BNRItem; • 在 data model inspector 中,勾选 To-Many Relationship
  • 21. • 给 BNRItem 实体增加⼀一个名为 assetType 的关系, 把 BNRAssetType 作为⺫⽬目的,在 Inverse 列,选择 items。
  • 23. • 当⼀一个对象被提取出来时,默认类型是 NSManagedObject,是 NSObject的⼀一个⼦子类,知 道如何跟 Core Data 互相操作。 • NSManagedObject 类似字典:持有实体中所有属 性的⼀一个 key-value pair。 • NSManagedObject 差不多就是⼀一个容器。如果我 们想让模型对象做更多⼯工作,我们必须⼦子类化 NSManagedObject。
  • 24. • 选中 BNRItem 实体,显⽰示 data model inspector, 并更改 Class 字段为 BNRItem。 • 现在当 BNRItem 实体被提取出来时,这个对象的 类型将会是 BNRItem。
  • 25. • 在 Finder 中,⾸首先备份好我们的 BNRItem.h 和 BNRItem.m ⽂文件 • 然后在 Xcode 中把这两个⽂文件从 project navigator 中删除。
  • 26. • 重新打开 Homepwner.xcdatamodeld • 选择 BNRItem 实体 • 然后选择从 New 菜单中选择 File,iOS -> Core Data, 选择 NSManagedObject subclass 选项,提⽰示 保存的时候,勾选“Use scalar properties for primitive data types”
  • 27. • Xcode 将会⽣生成两个新的 BNRItem.h 和 BNRItem.m ⽂文件 • 打开 BNRItem.h, 把 thumbnail 属性的类型改成 UIImage • 然后增加⼀一个跟之前⼀一样的⼀一个⽅方法声明 @interface BNRItem : NSManagedObject @property (nonatomic) int32_t itemName; @property (nonatomic, retain) NSString * serialNumber; @property (nonatomic) int32_t valueInDollars; @property (nonatomic) NSTimeInterval dateCreated; @property (nonatomic, retain) NSString * imageKey; @property (nonatomic, retain) NSData * thumbnailData; //@property UNKNOWN_TYPE UNKNOWN_TYPE thumbnail; @property (nonatomic, strong)UIImage *thumbnail; @property (nonatomic) double orderingValue; @property (nonatomic, retain) NSManagedObject *assetType; - (void)setThumbnailDataFromImage:(UIImage *)image; @end
  • 28. • NSDate 变成了 NSTimeInterval • 打开我们的 DetailViewController.h, 定位到 viewWillAppear:, 替换下⾯面的代码 // [dateLabel setText:[dateFormatter stringFromDate:[item dateCreated]]]; // 把 time interval 转换成 NSDate NSDate *date = [NSDate dateWithTimeIntervalSinceReferenceDate:[item dateCreated]]; [dateLabel setText:[dateFormatter stringFromDate:date]];
  • 29. • 然后,从旧的 BNRItem.m 中拷⻉贝 setThumbNailFromImage: ⽅方法到新的⽂文件 - (void)setThumbnailDataFromImage:(UIImage *)image { CGSize origImageSize = [image size]; // thumnail 的矩形⼤大⼩小 CGRect newRect = CGRectMake(0, 0, 40, 40); // 计算缩放⽐比 float ratio = MAX(newRect.size.width / origImageSize.width, newRect.size.height / origImageSize.height); // ⽣生成⼀一个带缩放因⼦子的透明位图上下⽂文 UIGraphicsBeginImageContextWithOptions(newRect.size, NO, 0.0); // ⽣生成⼀一个圆⾓角矩形路径 UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:newRect cornerRadius:5.0]; // 后续绘制 clip 到这个圆⾓角矩形 [path addClip]; // 图⽚片放到缩略图中间 CGRect projectRect; projectRect.size.width = ratio * origImageSize.width; projectRect.size.height = ratio * origImageSize.height; projectRect.origin.x = (newRect.size.width - projectRect.size.width) / 2.0; projectRect.origin.y = (newRect.size.height - projectRect.size.height) / 2.0; // 把图⽚片绘制上来 [image drawInRect:projectRect]; // 从图⽚片上下⽂文获得图⽚片,作为我们的缩略图保存 UIImage *smallImage = UIGraphicsGetImageFromCurrentImageContext(); [self setThumbnail:smallImage]; // 得到该图⽚片 PNG 形式,把它作为我们可以 archive 的数据 NSData *data = UIImagePNGRepresentation(smallImage); [self setThumbnailData:data]; // 完成以后,清除图⽚片上下⽂文资源 UIGraphicsEndImageContext(); }
  • 30. • 在 BNRItem.m 中重写 awakeFromFetch ⽅方法,来 从 thumbnailData 设置 thumbnail • 使⽤用 archiving 的时候我们是在 initWithCoder: 时 做的 - (void)awakeFromFetch { [super awakeFromFetch]; UIImage *tn = [UIImage imageWithData:[self thumbnailData]]; [self setPrimitiveValue:tn forKey:@"thumbnail"]; }
  • 31. • 我们创建⼀一个新的 BNRItem 实例时,它将会被加 ⼊入数据库。 • 当对象被添加到数据库时,它将被发送 awakeInsert 消息 • 在 BNRItem.m 中实现 awakeFromInsert • 原先我们是在 BNRItem 的 designated initializer 中 添加⼀一些附加⾏行为的 - (void)awakeFromInsert { [super awakeFromInsert]; NSTimeInterval t = [[NSDate date] timeIntervalSinceReferenceDate]; [self setDateCreated:t]; }
  • 34. • 在 BNRItemStore.h 中,导⼊入 Core Data 然后增加三 个实例变量 #import <Foundation/Foundation.h> // 导⼊入 Core Data 头⽂文件 #import <CoreData/CoreData.h> @class BNRItem; @interface BNRItemStore : NSObject { NSMutableArray *allItems; NSMutableArray *allAssetTypes; NSManagedObjectContext *context; NSManagedObjectModel *model; }
  • 35. • 在BNRItemStore.m 中,更改 itemArchivePath 的实 现来返回⼀一个不同的路径供 Core Data 存储数据 - (NSString *)itemArchivePath { NSArray *documentDirectories = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *documentDirectory = [documentDirectories objectAtIndex:0]; // return [documentDirectory stringByAppendingPathComponent:@"items.archive"]; return [documentDirectory stringByAppendingPathComponent:@"store.data"]; }
  • 36. • BNRItemStore 被初始化时,它需要设置 NSManagedObjectContext 和 NSPersistentStoreCoordinator • 我们需要创建⼀一个 NSManagedObjectModel 来持 有 Homepwner.xcdatamodeld 的实体信息,并且 使⽤用这个对象初始化 persistent store coordinator • 因此我们将创建 NSManagedObjectContext 的实 例,并且指定它使⽤用这个 persistent store coordinator 来保存和加载对象
  • 37. • 在 BNRItemStore.m 中更新 init - (id)init { self = [super init]; if (self) { //// allItems = [[NSMutableArray alloc] init]; // NSString *path = [self itemArchivePath]; // allItems = [NSKeyedUnarchiver unarchiveObjectWithFile:path]; // // 如果数组之前没有被保存,创建⼀一个新的空数组 // if (!allItems) { // allItems = [[NSMutableArray alloc] init]; // } // 读取 Homepwner.xcdatamodeld model = [NSManagedObjectModel mergedModelFromBundles:nil]; NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model]; // SQLite ⽂文件在哪⼉儿? NSString *path = [self itemArchivePath]; NSURL *storeURL = [NSURL fileURLWithPath:path]; NSError *error = nil; if (![psc addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) { [NSException raise:@"Open failed" format:@"Reason: %@", [error localizedDescription]]; } // ⽣生成 managed object context context = [[NSManagedObjectContext alloc] init]; [context setPersistentStoreCoordinator:psc]; // 不需要管理 undo [context setUndoManager:nil]; } return self; }
  • 38. • 之前我们使⽤用 keyed archiving 的时候,当我们要 求保存数据,BNRItemStore 将会写⼊入整个 NSMutableArray 的 BNRItems。 • 现在我们让它给 NSManagedObjectContext 发送 save: 消息。Context 将会更新 store.data 中从最后 ⼀一次保存其有任何变更的所有记录。 • 在 BNRItemStore.m 中,更改 saveChanges @implementation BNRItemStore - (BOOL)saveChanges { // // 返回成功或失败 // NSString *path = [self itemArchivePath]; // return [NSKeyedArchiver archiveRootObject:allItems toFile:path]; NSError *err = nil; BOOL successful = [context save:&err]; if (!successful) { NSLog(@"Error saving: %@", [err localizedDescription]); } return successful; }
  • 40. • 要从 NSManagedObjectContext 提取数据,我们需 要 prepare and execute ⼀一个 NSFetchRequest。 • 当这个 fetch 请求被执⾏行以后,我们将得到⼀一个符 合这个请求的 parameters 的所有对象的⼀一个数组 • fetch 请求需要我们想要从中获取对象的⼀一个实体 的描述。这⾥里我们指定 BNRItem 实体。
  • 41. • 也可以设置请求的 sort descriptors 来指定数组中 对象的顺序。 • ⼀一个 sort descriptor 具有⼀一个映射到⼀一个 attribute 的 key,和⼀一个表⽰示正序还是倒序的 BOOL 值 • 我们希望使⽤用使⽤用 orderingValue 正序排列返回的 BNRItem • 在 BNRItemStore.h 中,声明⼀一个新的⽅方法 - (void)loadAllItems;
  • 42. • 在 BNRItemStore.m 中定义 loadAllItems 来 prepare and execute ⼀一个 fetch 请求,并且保存结果到 allItems 数组 - (void)loadAllItems { if (!allItems) { NSFetchRequest *request = [[NSFetchRequest alloc] init]; NSEntityDescription *e = [[model entitiesByName] objectForKey:@"BNRItem"]; [request setEntity:e]; NSSortDescriptor *sd = [NSSortDescriptor sortDescriptorWithKey:@"orderingValue" ascending:YES]; [request setSortDescriptors:[NSArray arrayWithObject:sd]]; NSError *error; NSArray *result = [context executeFetchRequest:request error:&error]; if (!result) { [NSException raise:@"Fetch failed" format:@"Reason: %@", [error localizedDescription]]; } allItems = [[NSMutableArray alloc] initWithArray:result]; } }
  • 43. • 在 BNRItemStore.m 中,在 init 的末尾发送这个消 息给 BNRItemStore // 不需要管理 undo [context setUndoManager:nil]; [self loadAllItems]; } return self; }
  • 44. • 要想有选择性的 fetch ⼀一些实例,我们可以添加⼀一 个 predicate(⼀一个 NSPredicate)到我们的 fetch 请 求,只有满⾜足这个 predicate 的对象会被返回。 • predicate 包含的是⼀一个可以为 true 或 false 的条 件。例如: // ⼀一个 predicate 的例⼦子,设定 fetch 条件 NSPredicate *p = [NSPredicate predicateWithFormat:@"valueInDollars > 50"]; [request setPredicate:p]; • predicate 也可以被⽤用来过滤⼀一个数组的内容,例如 // predicate ⽤用来过滤数组的例⼦子 NSArray *expensiveStuff = [allItems filteredArrayUsingPredicate:p];
  • 46. • 要创建⼀一个新的 BNRItem,我们将请求 NSManagedObjectContex 从 BNRItem 实体插⼊入⼀一 个新的对象。它将返回⼀一个 BNRItem 的实例。 • 在 BNRItemStore.m 中,编辑 createItem ⽅方法 - (BNRItem *)createItem { //// BNRItem *p = [BNRItem randomItem]; // BNRItem *p = [[BNRItem alloc] init]; double order; if ([allItems count] == 0) { order = 1.0; } else { order = [[allItems lastObject] orderingValue] + 1.0; } NSLog(@"在 %d 个 items 之后添加,顺序是:%.2f", [allItems count], order); BNRItem *p = [NSEntityDescription insertNewObjectForEntityForName:@"BNRItem" inManagedObjectContext:context]; [p setOrderingValue:order]; [allItems addObject:p]; return p; }
  • 47. • 当⼀一个⽤用户删除了⼀一个 item 的时候,我们必须通 知 context,这样它也会被从数据库删除。 • 在 BNRItem.m 中,增加下列的代码到 removeItem: - (void)removeItem:(BNRItem *)p { // BNRItem 被从 store 中移除的时候,它的 image 也应该被从⽂文件系统删除 NSString *key = [p imageKey]; [[BNRImageStore sharedStore] deleteImageForkey:key]; // 增加通知 context 有数据被删除的代码 [context deleteObject:p]; [allItems removeObjectIdenticalTo:p]; }
  • 49. • 我们需要为 BNRItem 替换的最后⼀一点功能是在 BNRItemStore 中重新排序 BNRItems。 • 因为 Core Data 不会⾃自动化的处理排序,每次它在 table view 中被移动时我们必须更新 BNRItem 的 orderingValue。 • 在 BNRItemStore.m 中,修改 moveItemAtIndex:toIndex: 来处理重新排序 items
  • 50. - (void)moveItemAtIndex:(int)from toIndex:(int)to { if (from == to) { return; } // 得到被移动的对象的指针,以便我们可以把它重新插⼊入 BNRItem *p = [allItems objectAtIndex:from]; // 从数组中删除 [allItems removeObjectAtIndex:from]; // 在新的位置重新插⼊入 [allItems insertObject:p atIndex:to]; // Core Data 的排序 // 为被移动的对象计算⼀一个新的 orderValue double lowerBound = 0.0; // 数组中在它之前是否有⼀一个对象? if (to > 0) { lowerBound = [[allItems objectAtIndex:to -1] orderingValue]; } else { lowerBound = [[allItems objectAtIndex:1] orderingValue] - 2.0; } double upperBound = 0.0; // 数组中在它之后是否有⼀一个对象 if (to < [allItems count] - 1) { upperBound = [[allItems objectAtIndex:to + 1] orderingValue]; } else { upperBound = [[allItems objectAtIndex:to -1] orderingValue] + 2.0; } double newOrderValue = (lowerBound + upperBound) / 2.0; NSLog(@"moving to order: %f", newOrderValue); }