【进阶篇】如何优雅的设计coredata的离线缓存策略

让我们配合coreData来设计一个优雅的本地数据缓存策略。

【进阶篇】如何优雅的设计coredata的离线缓存策略

关乎到用户体验,离线缓存在一个完善成熟的App架构中,似乎成了不可或缺的部分,本地数据加载的零延迟,能给用户带来更好的使用体验。
在使用离线缓存数据时,第一个需要考虑的是数据的实时性
数据的实时性对于不同的App或者不同的信息类型,都会有不一样的要求,数据的实时性可以用来划分不同的场景或者使用离线缓存的策略:

1、数据实时性要求高:这类信息数据变化频繁,需要客户端与服务器的数据保持高度统一,这种情况下总是优先去请求网络数据。

注意:

因为这类数据通常“过期”会带来比较大的危险,所有如果显示了本地数据,应禁用对应的数据操作功能,并提示警告信息。

image1

2、数据实时性要求不高:数据在服务器的变化不会很频繁,客户端可以优先使用本地缓存数据,让用户去选择是否加载新的数据。通常这类数据在本地存在的时间越长,离线缓存的优势就越明显。

image2

3、权衡利弊:在一开始定好规则,在适当的时机或条件下,智能的去决定优先使用哪方面的数据。

image3

思考:

关于这一块,不同的业务场景,业务需求,数据显示逻辑,面对的可能都不一样,所有还是需要在实际的开发中去拿捏分寸,巧妙的将数据存在本地,而又避免“过期数据”带来的危险。

扯了这么多,如何使用coredata来做到这些事情呢?(这特么才是重点好么~)

前面已经写了两篇关于coredata的简单使用和coredata数据迁移的文章,现在就来谈谈如何进阶使用吧。

1、封装:

数据操作无非增删改查,这些方法必须进行封装,那么封装在哪呢?我们需要一个所有实体的基类:



/**
 *  实体基类
 */
@interface BaseEtt : NSManagedObject

//获取类名
+(NSString*)className;

//创建一个新的实体
+(BaseEtt*) createEntity;

//根据where条件查询实体
+(NSArray*) selectEntityWithWhere:(NSString*) where;

/**
 *  根据条件升序查询
 *
 *  @param where 条件
 *  @param key   排序字段
 *
 *  @return
 */
+(NSArray*) selectEntityWithWhere:(NSString*) where byAsc:(NSString *)key;
/**
 *  根据条件降序查询
 *
 *  @param where 条件
 *  @param key   排序字段
 *
 *  @return
 */
+(NSArray*) selectEntityWithWhere:(NSString*) where byDesc:(NSString *)key;

/**
 *  根据条件分页查询
 *
 *  @param page  页码
 *  @param where 条件
 *
 *  @return
 */
+(NSArray*) getListByPage:(PageInfo*)page where:(NSString*)where;

/**
 *  保存实体
 *  
 */
+(void) saveEntity;
/**
 *  删除实体
 *  @param entity
 */
+(void) removeEntity:(BaseEtt*) entity;
/**
 *  删除所有实体
 */
+(void) removeAll;
/**
 *  按条件删除实体
 *
 *  @param where
 */
+(void) removeWithWhere:(NSString*) where;
/**
 *  按条件删除实体保存
 *
 *  @param where
 *  @param save
 */
+(void) removeWithWhere:(NSString*) where SaveContext:(BOOL) save;

/**
 *  判断两个实体是否相等
 *
 *  @param entity 对比实体
 *
 *  @return 是否相等
 */
-(BOOL) isEqualToEntity:(BaseEtt*) entity;
@end

这里包含了常用的增删该查的方法,一般在手机端所需要的数据查询并不会怎么复杂,这些基本都能够满足日常使用,新建的实体继承这个基类,这样就可以很方便的使用这些方法了。

isEqualToEntity这个方法是用来比较服务器获取数据转化为实体后是否与本地的实体对象是否相等,所以需要在所有继承它的子类进行重写,至于条件的话,可以根据实际情况进行扩展,一般都是使用xxID之类的字段。

PageInfo是一个分页对象,里面就两个属性,一个PageIndex,一个PageSize

2、判断

这些数据大致会在三个地方:

1、dataArray(当前页面显示数据存放的数组)

2、tempArray(从服务器获取数据的临时存放数组)

3、localDB(本地数据库)

注:

这里不得不提的一点是,coredata是一个对象关系映射的数据持久化框架,它的实体对象是互相影响的,也可以说其实就是一个,所以dataArray的数据和localDB中的数据可以看成是一样的,dataArray只是拿了localDB一部分而已.

我模拟了以下几个常用的场景:

1、进入页面请求服务器数据,成功后的与本地数据的对比判断:

image4

2、上拉加载更多数据(分页)
image5

其它的判断场景大致都差不多,只需要注意:

1、不要添加重复数据到本地,这将可能会导致数据错乱

2、如果业务需要,一定要存储多条相同的数据在本地,那么要记得用不同的标记来区分,并且 isEqualToEntity 的重写应该要把这个标记的判断加进去。

3、一定要理解对象关系映射(ORM)的数据状态,注意实体对象的引用(例如:上级页面用到这某个对象,在下级页面将这个对象进行了remove,即使在你看来这是两个不同的对象了,也是会导致上个页面报空指针的异常)。

3、说点其它的:

其实标题中的优雅二字在我写完后发现并不合适,在写博客的过程中,有些之前没想到的东西突然也变得豁然开朗,不过还是不得不说一句,coredata的水比较深,慎入!!!(开玩笑)

附上github地址:coreDataTools