关于Block的几点思考

作者: 小笨狼 | 来源:发表于2018-05-30 20:57 被阅读103次

    背景

    block的问题算是老生常谈了,本以为对block很熟悉了,但是前几天跟同事聊到几个block的很有意思的点,一开始还真的感觉一脸懵逼。想了很久才想出来自以为能解释的原因,大家也可以一起探讨

    block内嵌block的变量捕获

    - (void)embeddedBlock
    {
        void (^blockA)() = ^{
            void (^blockB)() = ^{
                NSLog(@"%@",self);
            };
        };
    }
    
    

    上面是一段很有意思的代码,blockB会捕获self,这是大家都了解的,但是blockA会捕获self么?为什么?

    一开始听到这个问题也是一脸懵逼,到底外面的block会不会捕获self呢?

    我们来看源码:

    struct __ClassA__embeddedBlock_block_impl_0 {
      struct __block_impl impl;
      struct __ClassA__embeddedBlock_block_desc_0* Desc;
      // ③ 定义成员变量self
      ClassA *self;
      __ClassA__embeddedBlock_block_impl_0(void *fp, struct __ClassA__embeddedBlock_block_desc_0 *desc, ClassA *_self, int flags=0) : self(_self) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
      }
    };
    static void __ClassA__embeddedBlock_block_func_0(struct __ClassA__embeddedBlock_block_impl_0 *__cself) {
      ClassA *self = __cself->self; // bound by copy
    
                NSLog((NSString *)&__NSConstantStringImpl__var_folders_pv_88jpx_sn429_1nx8xzc8g7w00000gn_T_ClassA_64c0bc_mi_0,self);
            }
    static void __ClassA__embeddedBlock_block_copy_0(struct __ClassA__embeddedBlock_block_impl_0*dst, struct __ClassA__embeddedBlock_block_impl_0*src) {_Block_object_assign((void*)&dst->self, (void*)src->self, 3/*BLOCK_FIELD_IS_OBJECT*/);}
    
    static void __ClassA__embeddedBlock_block_dispose_0(struct __ClassA__embeddedBlock_block_impl_0*src) {_Block_object_dispose((void*)src->self, 3/*BLOCK_FIELD_IS_OBJECT*/);}
    
    static struct __ClassA__embeddedBlock_block_desc_0 {
      size_t reserved;
      size_t Block_size;
      void (*copy)(struct __ClassA__embeddedBlock_block_impl_0*, struct __ClassA__embeddedBlock_block_impl_0*);
      void (*dispose)(struct __ClassA__embeddedBlock_block_impl_0*);
    } __ClassA__embeddedBlock_block_desc_0_DATA = { 0, sizeof(struct __ClassA__embeddedBlock_block_impl_0), __ClassA__embeddedBlock_block_copy_0, __ClassA__embeddedBlock_block_dispose_0};
    
    struct __ClassA__embeddedBlock_block_impl_1 {
      struct __block_impl impl;
      struct __ClassA__embeddedBlock_block_desc_1* Desc;
      // ① 定义成员变量self
      ClassA *self; 
      __ClassA__embeddedBlock_block_impl_1(void *fp, struct __ClassA__embeddedBlock_block_desc_1 *desc, ClassA *_self, int flags=0) : self(_self) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
      }
    };
    static void __ClassA__embeddedBlock_block_func_1(struct __ClassA__embeddedBlock_block_impl_1 *__cself) {
      ClassA *self = __cself->self; // bound by copy
        // 将self从blockA中传入到blockB中,blockB捕获self
            void (*blockB)() = ((void (*)())&__ClassA__embeddedBlock_block_impl_0((void *)__ClassA__embeddedBlock_block_func_0, &__ClassA__embeddedBlock_block_desc_0_DATA, self, 570425344));
        }
    
    static struct __ClassA__embeddedBlock_block_desc_1 {
      size_t reserved;
      size_t Block_size;
    } __ClassA__embeddedBlock_block_desc_1_DATA = { 0, sizeof(struct __ClassA__embeddedBlock_block_impl_1)};
    
    static void _I_ClassA_embeddedBlock(ClassA * self, SEL _cmd) {
    // ②将self从外界传入到blockA中,blockA捕获self
        void (*blockA)() = ((void (*)())&__ClassA__embeddedBlock_block_impl_1((void *)__ClassA__embeddedBlock_block_func_1, &__ClassA__embeddedBlock_block_desc_1_DATA, self, 570425344));
    }
    
    

    可以看到,①和③处,blockA,blockB都定义了成员变量self。②,③处变量的传递,先将self从外界传入到blockA中。再从blockA的中传入到blockB中。

    blockB只能从blockA的作用域里捕获变量。因此blockB中捕获的任何东西,blockA必须也捕获一份。

    成员变量的捕获

    - (void)catchIvar
    {
        void (^blockA)() = ^{
            NSLog(@"%@",_ivarA);
        };
    }
    

    我们知道,上面的代码实际会捕获self。他跟下面的代码等价

    - (void)catchIvar
    {
        void (^blockA)() = ^{
            NSLog(@"%@",self->_ivarA);
        };
    }
    

    但是系统为什么要转化成self->_ivarA的形式去捕获self呢?而不是直接捕获_ivarA呢?他这么设计的目的在哪?

    这个问题我想了很久,最后想到了一个可以自圆其说的解释,也欢迎大家一起交流讨论

    正常的方法中,直接使用成员变量ivar,编译器都会自动转为self->ivar。之所以这样做,是因为正常的消息发生中,self是会作为一个参数,传递到所有方法中的。所以在任何方法方法中都可以直接获取到self。而成员变量ivar,并没有作为任何参数传入到方法中。所以为了访问到ivar,正常都会转化为self->ivar来访问。

    这个转化从iOS很早的版本就有了。而block是iOS4才开始出现。所以block里面的ivar转化为self->ivar,可能是为了跟其他地方保持一致性吧

    相关文章

      网友评论

        本文标题:关于Block的几点思考

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