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)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:];
}

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

你可能感兴趣的:(iOS 使用位段为你的协议实现缓存功能)