iOS教程:如何使用Core Data – 预加载和引入数据

这是接着上一次《iOS教程:Core Data数据持久性存储基础教程》的后续教程,程序也会使用上一次制作完成的。

再上一个教程中,我们只做了一个数据模型,之后我们使用这个数据模型中的数据创建了一个表视图,我们还学习了如何测试数据模型的可行性,今天,我们来看看如何在应用启动的时候,将已经存在的数据载入或者引用到我们的程序中去。

请注意我们在上一次的教程中学习到的是直接通过操作SQLite数据库来加载数据,你当然可以一直使用这种方法,但是这个教程教授的方法更加优雅,更加合理。

在下一部分的教程中,我们将会讨论如何使用NSFetchedResultsController来优化我们的应用的访问数据的方式。

至于如果你没有上一次做好的程序的话,你可以从这里下载。

预加载/引入数据

那么我们究竟怎样把数据存储进Core Data数据库呢?目前有两种比较好的选择。

  1. 在App启动的时候从外部文件引入数据,就是在程序开始运行的时候从外部的资源,比如SQLite数据库或者XML文件中,引入数据。
  2. 提供一个已经制作完成的SQLite数据库,首先制作一个像上次的教程说的那样的数据库模型,之后在这个模型中填充数据,填充数据的方式是使用一个utility app,这个utility app可以是一个使用Core Data API填充数据库的Mac或者iOS app,也可以是一些直接填充数据库的程序。一旦数据库被填充之后,你就可以在没有已存在的数据库的情况下设置这个数据库未使用的默认数据库。

在这个教程中,我们会通过第二种,为大家展示如何使用一个简单的utility app来预加载一个已经装在好的Core Data数据库,以便让你的app使用。

第一步

我们在iOS上使用Core Data的方法的基础和我们在Mac OS X上使用的是一致的,他们使用同样的模型和类。

这一为我们可以写一个MAC OS X上的简单的console程序,来从数据源引入数据,再把这个数据库的数据库拿来给我们的iOS程序来用,不错吧?

我们来试试,首先打开Xcode,在 Mac OSX类中的Application中使用Command Line Tool 的模板。

我们就用 “CoreDataTutorial2” 作为工程的名字吧,记得使用“Core Data” 和 “Use Automatic Reference Counting” 。

完成创建之后,选择 “CoreDataTutorial2.xcdatamodeld” 彻底删除之。

之后找到我们上次完成的哪些文件中的

  • FailedBankCD.xcdatamodeld
  • FailedBankInfo.h
  • FailedBankInfo.m
  • FailedBankDetails.h
  • FailedBankDetails.m

将这些文件复制,或者直接拖到我们的新项目中:

确保“Copy items into destination group’s folder (if needed)” 没有选中

并且选中“Add to targets” 。

选择 main.m,你会注意到由于我们选择了使用Core Data,所以这里为我们准备了一些模板的方法,现在,我们来修改这些方法来让他们为我们的iOS程序生成数据数。

将 managedObjectModel() 方法从

NSString *path = [[[NSProcessInfo processInfo] arguments] objectAtIndex:0];
path = [path stringByDeletingPathExtension];

替换为

NSString *path = @"FailedBankCD";

这会把这个程序指向 FailedBankCD.xdatamodeld 而不是我们已经删除的CoreDataTutorial2.xdatamodeld

按下command+r进行编译和运行,应该看到没有错误。

但是如果你在这一步之前进行过编译的话,你到这时就会出现数据不符的错误,按照我们上次的教程所说的那样,删除之后重新编译运行就行。

如果你看到了下面的错误:

NSInvalidArgumentException‘, reason: ‘Cannot create an NSPersistentStoreCoordinator
with a nil model‘

这是因为程序再找一个 ‘momd’ 文件, (上一个版本的Core Data模型),但是如果你的app使用的不是这个文件的话,那就会出这个错误,最快的修正方法就是把managedObjectModel()这个方法修改为下面的:

 NSURL *modelURL = [NSURL fileURLWithPath:[path stringByAppendingPathExtension:@"mom"]];

现在应该就没问题了

引入数据

现在到了动真家伙的时候了,真的把我们的数据加载进去。

在我们这个例子里,我们要从一个JSON文件中引入数据,也许你会想从其他类型的文件中引入数据,不过原理都是一样的。

下面,我们新建一个文件iOS – Other – Empty

把这个文件命名为Banks.json。

将下面的代码输进去:

[{ "name": "Bank1", "city": "City1", "state": "State1", "zip": 11111, "closeDate": "1/1/11" },
 { "name": "Bank2", "city": "City2", "state": "State2", "zip": 22222, "closeDate": "2/2/12" },
 { "name": "Bank3", "city": "City3", "state": "State3", "zip": 33333, "closeDate": "3/3/13" },
 { "name": "Bank4", "city": "City4", "state": "State4", "zip": 44444, "closeDate": "4/4/14" } ]

这是一个一个数组中包含四个字典的JSON文件,每一个字典都有几个与FailedBankInfo/FailedBankDetails中的物体相对应的属性。

如果你不是很清楚JSON文件是如何组织数据的,你可以看一下这个教程: this tutorial.

接下来,我们告诉我们的应用当编译的时候将这个文件我们的产品目录,看图做,首先选择Project,之后选择CoreDataTutorial2目标,选择Build Phase选项卡,按下“Add Build Phase”,选择“Add Copy File”,选择目标位置为“Products Directory”,最后,把“Banks。json拖到Add Files的部分。

当一个应用启动时,要先使用FailedBank的数据模型和类初始化一个Core Data的数据库,之后用Banks.json文件中的数据来输进去。

现在,我们要:

  • 载入 JSON 文件
  • 解析 JSON 文件为一个 Objective C 数组
  • 枚举这个数组中的数据,为每一个物体创建一个managed item。
  • 将他们全都存入 Core Data

编程开始,首先打开 main.m 把下面的代码加入主函数:

    NSError* err = nil;
    NSString* dataPath = [[NSBundle mainBundle] pathForResource:@"Banks" ofType:@"json"];
    NSArray* Banks = [NSJSONSerialization JSONObjectWithData:[NSData dataWithContentsOfFile:dataPath]
                                                     options:kNilOptions
                                                       error:&err];
    NSLog(@"Imported Banks: %@", Banks);

之后你的主函数看起来应该是下面这个样子的:

int main(int argc, const char * argv[])
{

    @autoreleasepool {
        // Create the managed object context
        NSManagedObjectContext *context = managedObjectContext();

        // Custom code here...
        // Save the managed object context
        NSError *error = nil;
        if (![context save:&error]) {
            NSLog(@"Error while saving %@", ([error localizedDescription] != nil) ? [error localizedDescription] : @"Unknown Error");
            exit(1);
        }

        NSError* err = nil;
        NSString* dataPath = [[NSBundle mainBundle] pathForResource:@"Banks" ofType:@"json"];
        NSArray* Banks = [NSJSONSerialization JSONObjectWithData:[NSData dataWithContentsOfFile:dataPath]
                                                         options:kNilOptions
                                                           error:&err];
        NSLog(@"Imported Banks: %@", Banks);

    }
    return 0;
}

这是用了使用了内置的 NSJSONSerialization API 来简单地将JSON的文件数据导入Core Foundation的数据类型中区(如NSArray,NSDictionary等等),想了解更多的话,请看 this tutorial.

运行一下这个程序,你会看到下面的输出:

2012-04-14 22:01:34.995 CoreDataTutorial2[18388:403] Imported Banks: (
        {
        city = City1;
        closeDate = "1/1/11";
        name = Bank1;
        state = State1;
        zip = 11111;
    },
        {
        city = City2;
        closeDate = "2/2/12";
        name = Bank2;
        state = State2;
        zip = 22222;
    },
        {
        city = City3;
        closeDate = "3/3/13";
        name = Bank3;
        state = State3;
        zip = 33333;
    },
        {
        city = City4;
        closeDate = "4/4/14";
        name = Bank4;
        state = State4;
        zip = 44444;
    }
)

现在我们已经能够把这些数据存储进了一个Objective – C的物体中,那么现在我们就可以像上次教程的末尾那样把这些数据输入进Core Data的数据库中。

首先在头部加上一下你需要的文件的引用语句:

#import "FailedBankInfo.h"
#import "FailedBankDetails.h"

之后把这些你之前加入主函数代码。

[Banks enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
    FailedBankInfo *failedBankInfo = [NSEntityDescription
                                      insertNewObjectForEntityForName:@"FailedBankInfo"
                                      inManagedObjectContext:context];
    failedBankInfo.name = [obj objectForKey:@"name"];
    failedBankInfo.city = [obj objectForKey:@"city"];
    failedBankInfo.state = [obj objectForKey:@"state"];
    FailedBankDetails *failedBankDetails = [NSEntityDescription
                                            insertNewObjectForEntityForName:@"FailedBankDetails"
                                            inManagedObjectContext:context];
    failedBankDetails.closeDate = [NSDate dateWithString:[obj objectForKey:@"closeDate"]];
    failedBankDetails.updateDate = [NSDate date];
    failedBankDetails.zip = [obj objectForKey:@"zip"];
    failedBankDetails.info = failedBankInfo;
    failedBankInfo.details = failedBankDetails;
    NSError *error;
    if (![context save:&error]) {
        NSLog(@"Whoops, couldn‘t save: %@", [error localizedDescription]);
    }
}];

// Test listing all FailedBankInfos from the store
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"FailedBankInfo"
                                          inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
for (FailedBankInfo *info in fetchedObjects) {
    NSLog(@"Name: %@", info.name);
    FailedBankDetails *details = info.details;
    NSLog(@"Zip: %@", details.zip);
}

这些代码本质上就是我们上一次使用的代码,除了我们这次使用了enumerateObjectsUsingBlock: 的方法老枚举这个数组的内容之后进行插入,之后我们使用一个Fetch命令来输出数据。

现在运行一下,你会看到输出了之前的数组。

2012-04-14 22:15:44.149 CoreDataTutorial2[18484:403] Name: Bank1
2012-04-14 22:15:44.150 CoreDataTutorial2[18484:403] Zip: 11111
2012-04-14 22:15:44.150 CoreDataTutorial2[18484:403] Name: Bank2
2012-04-14 22:15:44.151 CoreDataTutorial2[18484:403] Zip: 22222
2012-04-14 22:15:44.152 CoreDataTutorial2[18484:403] Name: Bank3
2012-04-14 22:15:44.152 CoreDataTutorial2[18484:403] Zip: 33333
2012-04-14 22:15:44.153 CoreDataTutorial2[18484:403] Name: Bank4
2012-04-14 22:15:44.153 CoreDataTutorial2[18484:403] Zip: 44444

Ok,这些就是你在Core Data中的数据了。除了这种简单的JSON文件之外,你也可以使用更加复杂的JSON文件,XML文件,甚至是普通的表格文件,只要你存成了csv的格式,也可以是来自互联网的pipe,可以使用的文件种类数也数不清,我们以后也会详细介绍。

Xcode犯病了?

下面我们做一个脑部移植手术,将我们使用Mac OS X上的命令行程序的数据库转移到iPhone app中去。最简单的找到数据库文件的方法就是右键(ctrl+) CoreDataTutorial2 产品揽之后按 “Show in Finder”。

这会打开一个新的Finder窗口,在这里面会有这些文件:

  • Banks.json – 这是数据的原始文件,记得吗?
  • CoreDataTutorial2 – 这个是应用本身。
  • FailedBankCD.momd (或者 .mom) – 这是编译好的Core Data数据模型。
  • CoreDataTutorial2.sqlite – 这就是我们在找的sqlite数据库文件,它是由程序生成的,Core Data应该可以通用的。你可以自己找一个SQLite数据库的查看软件,也可以下载 这个

确定 “CoreDataTutorial2.sqlite” 就是我么所需要的文件,下面我们把这个文件拷贝到我们上一个教程的源码工程文件之中,之后打开:

从Finder中拖拽 “CoreDataTutorial2.sqlite” 文件到Xcode的工程之中,确保 “Copy items into destination group’s folder (if needed)” 这个选项没有被选中,另一个是选中的。

最后,打开 “FBCDAppDelegate.m”,找到 persistentStoreCoordinator 方法,在 NSURL *storeURL = [[self app... 这一行的下面加入以下的代码:

if (![[NSFileManager defaultManager] fileExistsAtPath:[storeURL path]]) {
    NSURL *preloadURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"CoreDataTutorial2" ofType:@"sqlite"]];
    NSError* err = nil;

    if (![[NSFileManager defaultManager] copyItemAtURL:preloadURL toURL:storeURL error:&err]) {
        NSLog(@"Oops, could copy preloaded data");
    }
}

这一段的代码是为了检测sqlite数据库是否已经存在与这个app之中,如果不存在,就会找到我们预加载的数据库,之后把这个数据库复制到正常的路径,超级简单,来,让我们试试!

看到了原本在JSON文件中的四个Banks,之后还有一个我们在第一个教程中加入的Test Bank,如果你没有看到的话,八成是数据库已经存在了,山茶模拟器中的App之后重新运行。

之后看些什么?

这是我制作完成的例子程序源码,欢迎下载。

这是原作者的样板程序:here (direct download)

欢迎关注我的围脖: @Oratis

在知乎和豆瓣上,我的名字也是Oratis

我会把之后发表的教程分享到这些社交网络中。

如果你有任何问题,欢迎在底下留言,也欢迎写信给我,我的邮箱地址是: [email protected]

时间: 2024-10-17 15:59:02

iOS教程:如何使用Core Data – 预加载和引入数据的相关文章

iOS开发过程中使用Core Data应避免的十个错误

原文出处: informit   译文出处:cocoachina Core Data是苹果针对Mac和iOS平台开发的一个框架,主要用来储存数据.对很多开发者来说,Core Data比较容易入手,但很难精通,如果没有正确的学习方法,你将很难真正理解它,更不用说精通了.很多开发者常常在这方面犯一些错误,而这篇文章列出了开发者在iOS开发过程中使用Core Data常见的一些错误,并对如何避免这些错误进行了分析.  1.不了解关键术语 对于iOS开发者来说,会使用Core Data是一项必备技能.

预加载与智能预加载(iOS)

来源:Draveness(@Draveness) 链接:http://www.jianshu.com/p/1519a5302141 前两次的分享分别介绍了 ASDK 对于渲染的优化以及 ASDK 中使用的另一种布局模型:这两个新机制的引入分别解决了 iOS 在主线程渲染视图以及 Auto Layout 的性能问题,而这一次讨论的主要内容是 ASDK 如何预先请求服务器数据,达到看似无限滚动列表的效果的. 这篇文章是 ASDK 系列中的最后一篇,文章会介绍 iOS 中几种预加载的方案,以及 ASD

Oracle Coherence中文教程二十:预加载缓存

预加载缓存 本章介绍了不同的模式,你可以用它来预加载缓存.该模式包括批量装载和分布载荷. 本章包含以下各节: 执行批量加载和处理 执行分布式批量加载 20.1执行批量加载和处理 例20-5, PagedQuery.java ,演示了在一个连贯缓存技术,有效地批量加载和处理项目. 20.1.1批量写入缓存 使用连贯性时,一个常见的场景是预先填充缓存应用程序使用它之前.一个简单的方法来做到这例20-1中的Java代码所示: 例20-1预加载缓存 public static void bulkLoad

ios 预加载技术

方法二: 任何时候加载前后n张图片 以当前图片计算 问题 当网速慢的情况下 滑动特别快的情况下 还未加载完之前的图片 //区分 当前选择的页面 和当前加载的图片 是分开的 (这个是重点) 性能低下三 在创建列表页的时候,先不要创建里面的内容,当滑到当前页面的时候在创建 方法一:失败 我写不下去了 1.第一次加载的时候  加载左右两张 2.往左滑动的时候 预加载前面n张 3.往右滑动的时候 预加载后面n张 4.把已经加载的图片id存储起来  防止二次重新加载,做判断用 5.

Cocos2d-x教程(36)-多线程与异步加载

欢迎加入Cocos2d-x 交流群:193411763 转载时请注明原文出处 :http://blog.csdn.net/u012945598/article/details/41312345 ---------------------------------------------------------------------------------------------------------------------------------------------------------

刚入前端整合的一个手机端页面适配+预加载+获取资源加载进度等的一个小模板

刚入前端不久,之前主要学的是pc端的布局,到公司之后负责的主要是移动段页面,刚开始时为了使页面适应移动端不同的屏幕大小采用的是百分比加媒体查询的方式,做完一个项目之后,感觉非常不好,虽然最后也基本使页面做到了适配.所以做完这个项目之后,我就在网上查找各种屏幕适配的方案,最终找到了一个通过js控制使页面整体缩放的方案,还有一个就是通过js实时检测屏幕大改变html根字体大小的rem布局方案.目前我在使用的是缩放的方案.整体代码基本上是整合的是大牛们分享的一些实用代码,如有什么bug欢迎提出,共同进

没错预加载就是这么简单

之前项目中遇到图片加载问题,首先想到异步加载 ,接着想用什么框架,框架很多,我用的是volley,感觉挺方便的,废话不多说,记录一下自己的错误然后接着前行,用volley的时候虽然出现了效果,但是在出现默认图片之前还是出现几秒让人难以忍受的空白,不得不靠. 上代码: /** 在刷屏页 创建任务: private void getData(){ ReadImgTask task = new ReadImgTask(); task.execute(); } 接着: /** * 预加载跑马灯图片 *

jquery实现图片预加载

使用jquery实现图片预加载提高页面加载速度和用户体,本就为大家详细分析jquery图片预加载的实现原理. 什么时候使用图片预加载? 如果页面使用了很多不是最初加载便可见的图片,有必要进行预加载: $.preloadImages = function () { for (var i = 0; i < arguments.length; i++) { $('img').attr('src', arguments[i]); }};$.preloadImages('img/hover-on.png'

用javascript预加载图片、css、js的方法研究

预加载的好处可以让网页更快的呈现给用户,缺点就是可能会增加无用的请求(但图片.css.js这些静态文件可以被缓存),如果用户访问的页面里面的css.js.图片被预加载了,用户打开页面的速度会快很多,提升用户体验.在用到一些大图片展示的时候,预加载大图是很不错的方法,图片更快的被呈现给用户.不多说了,作为一个前端攻城师都懂的,下面分享我做的测试和得到的结果. 先说需要知道的服务器返回的status code:status-code: 200 - 客户端请求成功status-code: 304 -