斯坦福大学iOS开发公开课总结(十二 十三):CoreData,表格视图,Flickr摄影师资料列表Demo

第十二课和第十三课都介绍了CoreData的知识,并在十三课的中段通过一个Demo来具体实现了CoreData的操作。

笔者之前从未接触过Core Data的相关知识,因此学期这两节课比较吃力,这一篇总结还是有很多需要改进的地方,以后随着对Core Data认识的深入和对这两节课的反复咀嚼,会不断更新该总结。

开始吧!

Core Data


Core Data是一种持久化技术,它能将模型对象的状态持久化到磁盘,但它最重要的特点是:Core Data不仅是一个加载、保存数据的框架,它还能和内存中的数据很好的共事。

排除错误认识:Core Data并不是数据库! 它只是连接类(Class)和数据库(SQL)的桥梁。通过Core Data的相关功能,我们可以对数据库进行增删改查的操作。

CoreData是如何工作的呢?

1. 创建对象的可视化映射

在看到可视化映射之前,需要了解实体的概念:

实体的概念:每个实体是一个表,每个表对应一个对象。
简单粗暴的理解:实体在数据库领域叫做表,在面向对象领域叫做对象。

实体之间的关系=表之间创建关系,对象之间的关系。

注意:两个对象之间的关系在两个对象端具有不同的名称。而且关系的对应数量也是不同的:to one,to many

举个🌰 :摄影者和照片的关系:
摄影者对应多个照片,但是照片只对应一个摄影者。

那么言归正传,如何创建对象的可视化映射呢?

  1. 创建模型文件,用来装入各种需要映射的实体。
  2. 在模型内部添加实体(Entity),创建实体之间的关系(必要时)。

下面笔者录制了创建实体,增加实体属性,连接实体的操作:

创建实体,增加属性

2. 为实体创建NSManagedObjectd子类

我们需要将刚得到的可视化的实体“转变为”具体的类。在Core Data中,这些类都是NSManagedObjectd子类。

下面演示一下其创建过程:

创建NSManagedObjectd子类

以实体Photo为例,系统为我们生成了Photo.hPhoto.m

我们先看一下Photo.h:

``

#import

#import

NS_ASSUME_NONNULL_BEGIN
@interface Photo : NSManagedObject
// Insert code here to declare functionality of your managed object subclass
@end
NS_ASSUME_NONNULL_END

#import “Photo+CoreDataProperties.h”
``

我们可以看到,Photo类继承了NSManagedObject

但是,有意思的是,系统还为我们自动生成了Photo+CoreDataProperties.hPhoto+CoreDataProperties.m,详情见动图左侧,创建实体类之后。

思考
生成这两个文件的目的是什么呢?
首先,我们首先要知道这两个文件是什么:
他们构成了Photo类的分类(Category)。

那么什么是分类呢?
通过分类,我们可以向原有的类添加方法,而不需要通过继承的方式。分类的局限是:在分类里不能再添加属性。

那么显然,通过Photo+CoreDataProperties,我们就可以不用继承Photo类来给其添加方法。因为原有的Photo类只具有属性,除了获取属性之外,并不能为我们做其他的事情。这时,如果可以在其他的地方给其无限地添加方法还是很具有诱惑力的。令人欣慰的是,系统可以自动为我们生成。

3. 通过创建NSManagedObjectContext访问,操作数据库

数据库创建对象,设置对象属性,查询对象都需要NSManagedObjectContext

创建NSManagedObjectContext的两个不同的方法:

1.通过其自身的初始化:

[NSManagedObjectContext alloc] init];

2.通过UIManagedDocument创建:

UIManagedDocument 用于管理存储的机制,将Core Data数据库放入某存储空间。
UIManagedDocument *document = [[UIManagedDocument alloc] initWithFileURL:url]; //url:这个core data 数据库存储的地方

数据库的操作:

1. 向数据库添加对象(实体):

[NSEntityDescription insertNewObjectForEntityForName:@"Photo" inManagedObjectContext:context];

2. 从数据库删除对象(实体):

[aDocument.managedObjectContext deleteObject:photo];

删除对象后,系统会向所有对象发送这个消息

``

  • (void)prepareForDeletion{
    //在这里保持数据同步,比如删掉这个对象的时候会影响到其他对象的数据
    //应该在这个对象被删除前及时更新那个数据
    }

``

3. 在数据库查询对象(实体):

我们使用NSFetchRequest类查询数据库的对象,通过设置其不同属性来查找符合不同标准的数据:

举个🌰 :查找出100个photo的实体:

1
2
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Photo"];
request.fetchLimit = 100;

关于Core data的线程安全

1
2
3
4
//让context在安全队列中执行的方法
[context performBlock:^{
[A doSomething];
}];

NSFetchedResultsController


NSFetchedResultsController的作用是将NSFetchRequest 和 UITalbleView联系到一起。
和TableView的数据源方法类似:

1
2
3
4
5
6
7
- (NSInteger)numberOfRowsInSection:(NSInteger)section{
return [[self.fetchedResultsController sections] count];
}

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
return [[self.fetchedResultsController sections] count] objectAtIndex:section] numberOfObjects];
}

详细的使用方法会在Demo讲解部分中告诉大家。

Demo


Demo需求

  • 每隔20分钟,从flickr拿回最新的摄影者数据。
  • 用一个TableView显示当前拿回的摄影者的名字和所照的照片数。

Demo效果图

摄影师的信息列表

重要代码段

1. 在启动接口获取flickr的数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.photoDatabaseContext = [self createMainQueueManagedObjectContext];
[self startFlickrFetch];
return YES;
}

- (void)startFlickrFetch
{
[self.flickrDownloadSession getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
if (![downloadTasks count]) {
NSURLSessionDownloadTask *task = [self.flickrDownloadSession downloadTaskWithURL:[FlickrFetcher URLforRecentGeoreferencedPhotos]];
task.taskDescription = FLICKR_FETCH;
[task resume];
} else {
for (NSURLSessionDownloadTask *task in downloadTasks) [task resume];
}
}];
}

2. 每隔20分钟获取新的内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
- (void)setPhotoDatabaseContext:(NSManagedObjectContext *)photoDatabaseContext
{
_photoDatabaseContext = photoDatabaseContext;

//photoDatabaseContext设定成功后,每隔20分钟重新获取信息
if (self.photoDatabaseContext)
{
self.flickrForegroundFetchTimer = [NSTimer scheduledTimerWithTimeInterval:FOREGROUND_FLICKR_FETCH_INTERVAL
target:self
selector:@selector(startFlickrFetch:)
userInfo:nil
repeats:YES];
}

//photoDatabaseContext设定成功后 向控制器发送消息
NSDictionary *userInfo = self.photoDatabaseContext ? @{ PhotoDatabaseAvailabilityContext : self.photoDatabaseContext } : nil;
[[NSNotificationCenter defaultCenter] postNotificationName:PhotoDatabaseAvailabilityNotification
object:self
userInfo:userInfo];
}

3. 在表格视图查询所有摄影师的名字

1
2
3
4
5
6
7
8
9
10
11
12
- (void)setManagedObjectContext:(NSManagedObjectContext *)managedObjectContext
{
//哪个数据库
_managedObjectContext = managedObjectContext;

NSFetchRequest *requet = [NSFetchRequest fetchRequestWithEntityName:@"Photographer"];
requet.predicate = nil;//所有的,无过滤
requet.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"name" ascending:YES selector:@selector(localizedStandardCompare:)]];

self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:requet managedObjectContext:managedObjectContext sectionNameKeyPath:nil cacheName:nil];

}

4. 重写tablelViwe:cellForRowAtIndex:方法,显示摄影师数据

1
2
3
4
5
6
7
8
9
10
11
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Photographer cell"];

//拿到摄影师的名字和摄影数量
Photographer *photographer = [self.fetchedResultsController objectAtIndexPath:indexPath];
cell.textLabel.text = photographer.name;
cell.detailTextLabel.text = [NSString stringWithFormat:@"%lu photos", [photographer.photos count]];
return cell;

}

本Demo显然是一个未完成品,它只显示了摄影师的相关信息,并且只有一个页面。在接下来的课程中应该会对该Demo进行更多过的扩展。

最后的话


如果哪位小伙伴想拿到本文Demo的代码请不要客气,可以进入我的GitHub下载哦~ 这一系列到现在为止的所有Demo都在里面,分为英文注释版本和中文注释版本两种。

十分欢迎给笔者的代码和文笔抛出宝贵的意见和建议~

本文为笔者原创,如需转载,请事先与笔者交涉~

————————————————- 2018年7月17日更新 ————————————————-

注意注意!!!

笔者在近期开通了个人公众号,主要分享编程,读书笔记,思考类的文章。

  • 编程类文章:包括笔者以前发布的精选技术文章,以及后续发布的技术文章(以原创为主),并且逐渐脱离 iOS 的内容,将侧重点会转移到提高编程能力的方向上。
  • 读书笔记类文章:分享编程类思考类心理类职场类书籍的读书笔记。
  • 思考类文章:分享笔者平时在技术上生活上的思考。

因为公众号每天发布的消息数有限制,所以到目前为止还没有将所有过去的精选文章都发布在公众号上,后续会逐步发布的。

而且因为各大博客平台的各种限制,后面还会在公众号上发布一些短小精干,以小见大的干货文章哦~

扫下方的公众号二维码并点击关注,期待与您的共同成长~

公众号:程序员维他命

坚持原创技术分享,您的支持将鼓励我继续创作!