IOS Cache设计详细介绍及简单示例

2020-01-18 19:21:25刘景俊

cache的使用要有收有放,不能只创建不释放,事实上,所有涉及到data的操作都要考虑data的生命周期。我们做业务的时候,多是以Controller为基础单位,有些场景下,一个Controller在退出之后被再次进入的可能性就非常之低了,适时的清理cache会让我们App的整体表现更好。

Immutable Cache

Cache中存放的是啥?是Data。说到Data,就不得不提peak君最爱啰嗦的”Immutability(不可变性)”了,Immutability和我们代码的稳定性有着极大的关系,大到就像「房间里的大象」,很重要也容易被忽视。

在实践Immutability的时候,需要先将Data做分类,再去区分每一种类型Data如何去实施不可变性。做Data分类最重要的是分清楚值类型和引用类型的差别。传值的时候传递的是新的内存拷贝,所以值类型大多是安全的,传指针的时候传递的是同一块共享内存空间,这也是指针之所以危险的一大原因。bool,Int,long等等这些primitive type都是值类型,可以放心的传递,而对象类型往往是以指针的形式在传递,需要特别的注意,我们一般通过copy的方式(生成新的内存拷贝)来传递。这也是为什么Swift中将很多原先在Objective C中基础类变为值类型的原因,强化Immutability,让我们的代码更加安全。

我们看下不同类型的数据在Cache中的读写操作。

值类型-读

值类型可以安心返回:


- (int)spaceshipCount
{
  //...
  return _shipCount;
}

值类型-写

值类型也可以安全的写:


- (void)setSpaceshipCount:(int)count
{
  _shipCount = count;
}

对象类型-读

指针类型需要生成新拷贝:


- (User*)luckyUser
{
  //...
 return [_luckyUser copy];  
}

对象类的copy方法需要我们手动实现NSCopying protocol,开发的初期虽然显得繁琐了些,但后期的回报很大。而且这里的copy必须是deep copy,User中的每一个被持有的property都需要递归copy。

对象类型-写

对象类型写操作的危险之处在于函数的入参,入参也是对象类型的话,传入的是一个共享的引用:


- (void)setLuckyUser:(User*)user
{
  //...
  _luckyUser = [user copy]; 
}

集合类型-读

集合类也需要copy,是bug和crash的重灾区:


- (NSArray*)hotDishes
{
 //...
  return [_hotDishes copy];
}

集合类型-写


- (void)setHotDishes:(NSArray*)dishes
{
 //...
 _hotDishes = [dished copy];
}

看到这里,大家可能也发现了,其实原则也比较简单,只要保证业务模块从Cache中获取的数据都是独立的copy,就能避免数据共享带来的各种隐患。Cache模块有点类似函数式编程中的纯函数,既不依赖于外部的状态,也不会修改外部的状态,重点处理每一个函数调用的input(入参)和output(返回值)即可。