美文网首页iOS 进阶
代理,block,通知具体区别,使用场景分析

代理,block,通知具体区别,使用场景分析

作者: LPL_d5fc | 来源:发表于2020-05-11 17:37 被阅读0次

    一、代理

    代理是一种设计模式,一般是一对一,需要创建协议,执行方要遵守协议,实现相应的协议方法。协议和实现是分开的,可读性差一些,但是可维护性高,代理更倾向于过程信息的传输:例如scrollView的将要滚动、开始滚动、开始减速、停止,代理的不同方法,跟踪不同的状态。

    代理运行成本低,delegate只是保存一个指针,直接回调,没有额外的消耗。

    二、block

    block本质是OC的对象,也是一对一,使用比较简便,快捷,可读性好一些。block可以获取上下文数据,效率比较高,block效率高是因为block是内联的。block比较注重传输的结果,成功与否,而不是像代理一般关注过程。block效率高是因为block是内联的。

    block运行成本高,block出栈时,要将使用的数据从栈内存拷贝到堆内存,对象的话就是引用计数加一,导致运行成本很高。

    从上面的描述可以看出两者的区别。

    三、通知

    通知利用了观察者模式,一对多消息传递,没有回调,可以在发送通知过程中,传递一些数据。一般的使用场景是,在一个界面进行某种操作,然后要发出通知,通知接收方以及接收到通知的操作是不在当前界面执行的,一般使用通知。

    四、总结一下:

    1.代理倾向于过程,block倾向于结果。

    2.代理书写起来繁琐一些,block很便捷。

    3.代理运行成本低,block运行成本高。优先使用block(单次,或回调很少,不频繁),如果需要频繁的回调,数量比较多的回调,例如tableView的代理方法,界面滚动的时候会反复调用,就适合用代理。单次,或者比较少的回调适合用block。有利于优化性能。

    4.通知一般的使用场景是,在一个界面进行某种操作,然后要发出通知,通知接收方以及接收到通知的操作是不在当前界面执行的。

    内联函数提高效率?(摘抄大佬笔记)

    我们都知道,一个函数的调用要付出时间上的代价,其大致过程为:

    1. 保护现场,就是先将主调函数里的函数调用返回后要执行的指令的地址压入栈中保存;

    2. 把被调函数的形参和auto存储类型的变量压入栈区保存,这一步压入的所有变量所占有的存储我们称之为被调函数的数据现场;

    3. 执行完被调函数之后,把被调函数数据现场释放(出栈);

    4. 把第1步压入的指令地址出栈,即恢复现场,然后找到这个地址继续执行。

    要是一个函数被调用了许多次,那么编译系统需要来来回回的往返许多趟,存在栈内存创建和释放的开销,于是C++(C99)的编译系统提供了一个大袋子。在被调函数的定义前加一个标志(inline)告诉编译系统,编译系统看到这个标志后,实际编译出的可执行程序,就如同用函数体合理地置换了函数被调用处一样,我们称之为内联机制。

    内联机制可以消除函数调用和返回带来的开销(寄存器存储和恢复),另外,编译器也会把调用函数的代码和函数本身放在一起进行优化,那么,是不是干脆把所有的函数都定义称内联函数呢,天底下没有免费的午餐,在计算机科学中,空间和时间永远是个矛盾体。内联是以代码膨胀(复制)为代价,仅仅省去了函数调用的开销,需要占用更多的内存空间或者更多的指令缓存,最终生成的可执行程序的体积会有膨胀,这是典型的以空间换时间的做法。

    这是对于函数体比较短小的情况,但如果函数体比较大,执行函数体内代码的时间,相比于函数调用的开销比较大,那么效率的收获会很少,而且,每一处内联函数的调用都要拷贝一份代码,将使程序的总代码量增大,消耗更多的内存空间,这样就不值得了。

    一般来说,具有以下特征的函数适宜定义成内联函数:

    1. 函数体内的语句数量较少,如果函数体内代码比较长,使用内联将导致内存消耗代价较高;

    2. 函数体内没有循环,递归等。出现循环,执行函数体内代码的时间要比函数调用的开销大;

    3. 在满足上面两个前提下,函数实际调用次数却很多的情况。就可以考虑将函数定义为内联来提高效率。

    那么内联函数究竟是如何工作的呢?

    当我们定义了一个函数之后,编译器会将其编译成一个指令集合。这个指令集合在程序运行的时候会出现在内存的代码区里,并且在调用此函数时程序执行的地址会跳转到这个指令集合的入口地址,当指令集合执行完后,再跳回到主调函数。换句话说,任何时候内存中只有一个指令集,如果该函数被调用10次,则运行时就会跳转到同一入口地址10次。

    如果定义为inline函数,编译器并不创建真实函数,内联函数不仅同普通函数一样经过检查后保存函数名称、参数类型和返回值类型,还会把内联函数的本体也一并存入符号表中,在之后的编译过程中一旦遇到该函数被调用时会首先检查调用是否合法,然后编译器会将inline函数的指令集合(函数代码)复制嵌入到主调函数中的调用位置,内联函数的代码就会直接替换函数调用,这样就不需要函数调用的跳转开销了。如果函数被调用了10次,就相当于内存中就包含10个相同指令集合的拷贝,没有一次调用。

    了解了内联函数是怎么工作的,那么内联机制的优劣就好理解了。需要清楚的是,我们定义为inline函数只是建议编译器进行内联,而不是命令编译器进行内联,所以最后是不是内联函数取决于编译器。还有关键字inline必须与函数定义放在一起才能使函数成为内联(最后由编译器决定),仅放在函数声明前面不起作用。因为inline是在编译时展开,必须有实体,在编译阶段,编译器看到inline标志就会根据该函数体情况去判断是否应该将该函数体定义为内联。没看到本体怎么判断勒。

    inline是一种“用于实现的关键字”,内联函数不能带有virtual关键字。常把inline函数定义为static类型,可以在不同文件中定义同名文件,而不必担心命名冲突,所有未加static前缀的函数都具有全局可见性,其他源文件也能访问。

    ————————————————

    版权声明:本文为CSDN博主「selfimpr1991」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

    原文链接:https://blog.csdn.net/wenqian1991/article/details/17201555

    相关文章

      网友评论

        本文标题:代理,block,通知具体区别,使用场景分析

        本文链接:https://www.haomeiwen.com/subject/fmrrnhtx.html