iOS 使用位段为你的协议实现缓存功能

在设计接口的时候,委托是常用的交互方式,一般的,我们在使用协议时会写出以下代码。

if ([delegate respondsToSelector:@selector(someMothod:)]) {
        [delegate someMothod:(id)sender];
    }

一般情况下没有问题,事实上,以上代码可以满足大部分的需求,但是假设你所实现的协议方法是一个类似网络下载进度回调的功能,每隔很短的小段时间就需要回调一次呢?

你会发现,除了第一次检测委托对象是否响应某个选择子有意义外,之后的检测都是多余的。如果委托对象本身没变,那么不大可能会忽然不能响应某个选择子。鉴于此,我们可以把委托对象能否响应某个协议方法这一信息缓存起来,已优化程序效率。

假设现在有一个进度回调的协议方法

- (void)networkDidUpdateProgressTo:(float)progerss;

以上方法由于需要监听进度,需要频繁地回调progress参数,如果能够缓存委托对象能否响应的信息,我们就不必每次都去执行以下函数

if ([delegate respondsToSelector:@selector(someMothod:)])

将方法相应能力缓存起来的最佳途径就是使用C语言的“位段”数据类型。

这里简单介绍一下位段:
什么是位段呢? 位段是 C 语言特有的数据结构, 它允许我们定义一个由位组成的段, 并可为它赋以一个名字。
二进制位是数据的基本单位,它比字节还小,一个字节由 8 位组成, 而在某些计算机系统中则可能是 16 位。
事实上,如果需要标志一个信息,一位就足够了,但是由于字节是存储的最小单位,所以所有的变量至少要使用一个字节(比如BOOL值)。
如果我们想在一个很大的表中存储很多标志, 那么 "被浪费" 的内存空间是很可观的。幸运的是,在 C 语言中, 我们可以使用叫做位段的构造类型来定义一个结构体,从而定义某个字段所用的二进制位个数为某个特定的值。

struct data{                           //包含位段的结构体
    unsigned int fieldA : 8;    //位段fieldA,占8二进制位
    unsigned int fieldB : 4;    //位段fieldB,占4二进制位
    unsigned int fieldC : 2;    //位段fieldC,占2二进制位
    unsigned int fieldD : 1;    //位段fieldD,占1二进制位
}
//位段列表的形式为: 类型说明符 位域名:位域长度

以上结构体中,fieldA 位段将占用 8 个二进制位,fieldB 则为 4个,以此类推。于是,fieldA 可以表示 0 至 255 之间的值,而fieldD可以表示 0 或 1 这两个值。

我们可以像 fieldD 这样,把委托对象是否实现了协议中的相关方法这一信息缓存起来。如果创建的结构体中只有大小为 1 的位段,那么就能把很多 Boolean 值塞入一小块数据里面了(原来存一个BOOL值的空间,现在能存8个),我们现在文件中声明一个结构体。

struct {
    unsigned int delegateMothod1 : 1;
    unsigned int delegateMothod2 : 1;
    unsigned int delegateMothod3 : 1;
}  _delegateFlags;

然后重写我们的delegate的setter方法:

-(void)setDelegate:(id<personalDelegate>)delegate
{
    _delegate=delegate;
    _delegateFlags.delegateMothod1=[delegate respondsToSelector:@selector(delegateMothod1:)];
    _delegateFlags.delegateMothod2=[delegate respondsToSelector:@selector(delegateMothod2:)];
    _delegateFlags.delegateMothod3=[delegate respondsToSelector:@selector(delegateMothod3:)];
}

现在委托对象如果能够相应协议方法,位段就可以将其以一位的大小缓存起来,然后我们在之后调用委托对象的相关方法时,就不用检测委托对象是否能响应给定的选择子了,而是直接查询结构体里的标志:

if (_delegateFlags._delegateFlags.delegateMothod1){
    [_delegate delegateMothod1:];
}

在相关方法要调用很多次的时候,值得进行这种优化,而是否需要优化,则应依照具体代码来定。这需要分析代码性能,并找出瓶颈,若发现执行速度需要改进,则可使用此技巧。如果要频繁通过数据源协议从数据源中获取多分相互独立的数据,那么这项优化技术极有可能会提高程序效率。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 国家电网公司企业标准(Q/GDW)- 面向对象的用电信息数据交换协议 - 报批稿:20170802 前言: 排版 ...
    庭说阅读 11,096评论 6 13
  • *面试心声:其实这些题本人都没怎么背,但是在上海 两周半 面了大约10家 收到差不多3个offer,总结起来就是把...
    Dove_iOS阅读 27,200评论 30 471
  • 从三月份找实习到现在,面了一些公司,挂了不少,但最终还是拿到小米、百度、阿里、京东、新浪、CVTE、乐视家的研发岗...
    时芥蓝阅读 42,341评论 11 349
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,742评论 18 399
  • 早上经彭老师确认没有下雨,我蹬上运动鞋,背上小包包,准备步行上班,到了楼下一看,明明淅淅沥沥下着雨,赶紧跑上楼...
    瘪瘪元子阅读 262评论 1 3