美文网首页
[Note] Effective OC - Item 37~39

[Note] Effective OC - Item 37~39

作者: _lemon | 来源:发表于2015-12-19 22:52 被阅读61次

    Chapter 6. Blocks and Grand Central Dispatch

    <br />


    Item 37: Understand Blocks

    <br />
    这一节讲block的一些比较基本的内容。
    block也是一个OC对象,所以也会有引用计数以及保留环的问题。特别是在块内部引用或修改iVar的时候要特别小心,因为此时block也指向了iVar归属的那个对象,如果这个对象(比如常见的self)也指向了block,就会造成保留环。
    block的存储结构:


    像其他OC对象一样,isa指针指向类。invoke是一个函数指针,函数的第一个参数代表的就是block,类型是void *,函数指针指向block的实现代码。descriptor指针指向一个结构体,里面是描述block特征的一些参数。block里引用的变量都会被copy到“Captured Varaibles”这部分里,这里用的是浅拷贝,就是只是给变量多加了一个由block指向的指针。
    block默认是放在栈里,就类似全局变量,在大括号结束以后就可能被释放掉。如果想避免block被释放引起的bug,就要用copy把block复制到堆上。文中的例子:

    void (^block)();
    if (/* some condition */) {
       block = [^{
           NSLog(@“Block A”);
       } copy];
    } else {
       block = [^{
           NSLog(@“Block B”);
       } copy];
    }
    block();
    

    最后提到的是global block。没太见过,搜了一下没搜到太多结果,不知道是不是常用。是一类不需要引用外界的变量,内部实现在编译期可以完全确定的block。因为这种特点,所以可以直接优化分配到静态内存的部分,就像global/static变量,也类似单例。这样的block就可以直接使用,复制的话就增加指针个数,不用再创建新的block到栈中。
    <br />


    Item 38: Create typedefs for Common Block Types

    <br />
    这一篇讲用typedef給block起个名字的重要性。
    看到这我又想起最早的时候看《Programming with Objective-C》的时候遇到的例子了。例子长这样子:

    void (^(^complexBlock)(void(^)(void)))(void) = ^(void(^aBlock)(void)) {
        …
       return ^{
        …
       };
    };
    

    化简完是这样:

    typedef void (^XYZSimpleBlock)(void);
    XYZSimpleBlock (^betterBlock)(XYZSimpleBlock) = ^ (XYZSimpleBlock aBlock) {
        …
       return ^{
        …
       };
    }
    

    当时直接就蒙圈了。其实现在看还是需要绕一会。感谢当时大神帮我找了个解释,在这里:http://www.cocoawithlove.com/2009/10/ugly-side-of-blocks-explicit.html 简单来说,block的写法和函数指针的写法是类似的,用读函数指针的顺序来读block就可以看懂而且看得更清晰。上面的例子里最难看懂的一步是^(^complexBlock)(void(^)(void))表示的是complexBlock的返回值是一个block类型。实际中恐怕也不会遇到这样的例子,只是因为印象太深了所以提一下。
    用typedef给block起名字的好处有:

    • 首先肯定是易读多了。
    • 然后是需要修改的时候,可以先修改typedef的地方,然后根据报错一个一个去修改,不会遗漏。

    另外命名方面,如果block所做的事情是某个类特有的,就应该把类名写在前面。即便block的形式相同(指参数个数与类型、返回值类型),在不同的类里起不同作用时也应该分别去定义。如果是兄弟关系的类使用相同的block,则应定义在父类里。
    <br />


    Item 39: Use Handler Blocks to Reduce Code Separation

    <br />
    这一节讲block作为参数的好处,handler block是比较常见的一类情况。
    block作为参数直接展开写在方法调用里,好处是写起来方便,还使代码变得紧凑,block的内容和调用它的对象都写在一起。这也是设计block的初衷之一,对比代理和函数指针,这一点的优势尤其明显。
    Handler blocks在文中主要指网络请求完毕的回调block。一般有两种设计,一种是把成功和失败作为两个参数,各写一个block进行处理。另一种是只写一个的completion block,通过传入error变量来判断不同的情况然后分别处理。
    比较推荐的是后一种设计,和官方的风格比较相似,而且处理更加灵活,不是简单地二分为”成功“或”失败“,可以处理更多的情况,比如返回的数据不符合要求等。
    另外一个用法是,用在某些必须指定在某个线程运行的方法上。举例是通知中心的一个方法:

    -(id)addObserverForName:(NSString *)name object:(id)object queue:(NSOperationQueue *)queue usingBlock:(void (^)(NSNotification *))block
    

    这里第三个参数就是确定要在哪个队列运行,最后一个参数表示要在这个队列运行的代码块。

    相关文章

      网友评论

          本文标题:[Note] Effective OC - Item 37~39

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