美文网首页
OC52个有效方法 块与大中枢派发

OC52个有效方法 块与大中枢派发

作者: helinyu | 来源:发表于2021-09-13 16:10 被阅读0次

    37、理解“块”这一概念

    typedef void(^VoidBlock)(void);
    typedef void(^StringBlock)(NSString *value);
    

    1》块是C/c++/OC 的此法闭包
    2》块可接受参数,也可返回值
    3》块可以分配在栈或堆上,也可以是全局的。 分配在栈上的块可拷贝到堆里,这样的话,就和标准的OC对象一样,具备引用计数了。

    块的基础知识

    枚举方法、常用的方法末尾,都使用“内联块” 。

    块的内部结构:


    块本身也是对象

    《1》invoke: 函数指针,指向块的实现代码。
    《2》函数原型至少要接受一个void*型参数,此参数代表块。
    《3》块其实就是一种代替函数指针的语法结构,原来使用函数指针时,需要用“不透明的void指针”来传递状态。 而改用块之后,则可以吧原来用标准C语言特性所编写的代码封装成简明且易用的接口。
    《4》descriptor变量是指向结构体的指针,每个块都包含此结构体,其中声明了块对象的总体大小,还声明了copy与dispose这两个辅助函数所对应的函数指针。
    《5》块还会把它所捕获的所有变量拷贝一份。 这些拷贝放在descriptor变量后面, 捕获了多少个变量,就要占据多少内存空间。 Note:拷贝的并不是对象本身,而是指向这些对象的指针变量。 invoke函数为何需要把块对象作为参数传递进来呢?原因就在于: 执行块时,要从内存中把这些捕获到的变量读出来。

    全局块、栈块以及堆块

    栈:系统自动回收的。
    堆: 相当于一个对象,有对应的引用计数。
    全局:编译的时候就确定了,所以在程序结束的时候才回收。


    38、为常用的块类型创建typedef

    1》 以typedef重新定义块类型, 可令块变量用起来更加简单
    2》定义新类型应遵从现有额命名习惯,勿使用其名称与别的类型相冲突
    3》不妨为同一个块签名定义多个类型别名。 如果要重构的diamante使用了块类型的某个别名,那么值需要修改相应的typedef中的块签名即可,无需改动其他typedef。

    因为块的语法非常难记, 所以,我们可以取别名简化定义。


    39、用handler块降低代码分散程度

    1》 在创建对象时, 可以使用内联的handler块将相关业务逻辑一并声明
    2》在有多个实例需监控时,如果采用委托模式,那么经常需要传入的对象来切换,而若修改handler块来实现,则可直接间更快与相关的对象放在一起
    3》设计API时如果用到了handler块,那么可以增加一个参数,使调用者可以通过此参数来决定应该把块安排在哪个队列上执行。

    常常用到的异步线程,防止在网络请求的时候,阻塞主线程。
    异步方法在执行任务完成之后,需要以某种手段通知相关代码。 试下此工鞥呢有很多办法,常用的技巧是涉及一个委托协议
    委托协议,在tableView上有经典的使用。 并不会错误,但是存在的问题是代码比较分散。 一般是调用次数比较多的,为了提高性能,就会使用了delegate。
    委托模式有个缺点:如果类要分别使用多个获取器下载不同数据,那么就得delegate回调方法里根据传入的获取 其参数来切换。


    不仅令代码变长,而且还要吧挽留过数据获取器对象保存为实例变量,以便于判断语句中使用。

    改为块: 无需在回调方法里切换。 每个handler的业务逻辑,都是和相关的获取器对象一起来定义的:
    block的好处: 集中业务代码,代码更加简洁。



    block 可以把成功的数据和失败的数据合成block返回,也可分开处理。
    —— 一般推荐集合返回处理。
    写成为一个:
    缺点:代码比较长,且比较复杂。
    好处:更为灵活。 eg:数据下载到一半,网络出现故障。 (2)还有一个好处是如果返回数据太短,这个和网络获取数据失败可以作为同一种情况处理。
    (3)某些代码必须云心在特定线程上。 eg:UI必须在主线程上。 最好由调用API的人来决定handler应该运行在哪个线程上。

    这里说的有点不太明白,delegate 完全也是可以由调用者决定运行在哪个线程上的
    image.png

    40、用块引用其所属对象时不要出现循环引用

    1》如果块所捕获的对象直接或简洁的保留了块本省,就得当心循环问题
    2》一定要找个适当的时机解除循环应用,而不能包责任推给API的调用者。

    可以使用weak的方式声明、或者在执行block完之后,将block=nil.


    41、 多用派发队列, 少用同步锁

    1》派发队列可以用来表述同步语义(synchronization semantic),这种做法要比使用@synchronized 块或NSLock对象更简单
    2》将同步与异步派发结合起来,可以实现与普通枷锁机制一样的同步行为,而这么做却不会阻塞执行异步派发的线程
    3》使用不同队列以及栅栏块,可以令同步行为更加高效。

    [有关内容需要补充]


    42、多用GCD,少用performSelector系列方法

    1》performSelector 系列方法在内存管理方面容易疏失。 它无法确定将要执行的选择子具体是什么, 因为ARC编译器也就无法插入适当的内存管理方法
    2》performSelector 系列方法所能处理的选择子太过于局限了,选择子的返回值类型以及发送给方法的参数个数都受到限制
    3》如果想把任务放在另一个线程上执行,那么最好不要用performSelector系列方法,而是应该把任务封装到块里,然后调用大中枢派发机制的相关方法来实现。
    【内容需要补充】


    43、掌握GCD以及操作队列的使用时机

    1》要解决多线程与任务管理问题时, 派发队列并非唯一方案
    2》操作队列提供了一套高层的OC API, 能实现纯GCD所具备的绝大部分功能, 而且还能完成一些更为复杂的操作,哪些操作若改用GCD来实现,则需要另外编写代码。
    [内容需要补充]


    44、通过dispatch group 机制,根据系统资源状况来执行任务。

    1》一系列任务可归入一个dispatch group 之中。开发者可以在这组任务之心完毕时获得通知。
    2》通过dispatch group , 可以在并发式派发队列里同时执行多项任务。此时,GCD会根据系统资源装快来调度这些并发的任务。 开发者若自己来实现此功能,则需要编写大量代码。


    45、使用dispatch_once 来执行只需要运行一次的线程安全代码。

    单例模式




    上面是两种方式实现的单例, 发现后者比前者速度更加快。

    1》经常需要编写“只需要执行一次的线程安全代码”。通过GCD所提供的dispatch_once 函数,很容易就能够实现此功能
    2》标记应该声明在static或global作用于中, 这样的话,在吧只需要之心一次的块传给dispatch_once函数时,穿进去的标记也是相同的

    46、不要使用dispatch_get_current_queue

    相关文章

      网友评论

          本文标题:OC52个有效方法 块与大中枢派发

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