Block探索

我们使用的block分三种:静态block(没有使用任何外部变量),栈block(使用外部临时变量),堆block(使用外部成员变量或者属性)。

  1. 静态block

下面是测试的源码

@interface MBlockObj : NSObject

@end

#import "MBlockObj.h"

@implementation MBlockObj

- (void)testMBlock {
    
    void (^blockM)(void) = ^{
        
        int i = 0;
        i ++;
        
    };
    blockM();
    
}

@end

用终端执行 clang -rewrite-objc MBlockObj.m 命令后,我删去多余代码,得到下面这部分

struct __MBlockObj__testMBlock_block_impl_0 {
  struct __block_impl impl;
  struct __MBlockObj__testMBlock_block_desc_0* Desc;
  __MBlockObj__testMBlock_block_impl_0(void *fp, struct __MBlockObj__testMBlock_block_desc_0 *desc, int flags=0) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __MBlockObj__testMBlock_block_func_0(struct __MBlockObj__testMBlock_block_impl_0 *__cself) {

        int i = 0;
        i ++;

}

static struct __MBlockObj__testMBlock_block_desc_0 {
  size_t reserved;
  size_t Block_size;
} __MBlockObj__testMBlock_block_desc_0_DATA = { 0, sizeof(struct __MBlockObj__testMBlock_block_impl_0)};

static void _I_MBlockObj_testMBlock(MBlockObj * self, SEL _cmd) {

    void (*blockM)(void) = ((void (*)())&__MBlockObj__testMBlock_block_impl_0((void *)__MBlockObj__testMBlock_block_func_0, &__MBlockObj__testMBlock_block_desc_0_DATA));
    ((void (*)(__block_impl *))((__block_impl *)blockM)->FuncPtr)((__block_impl *)blockM);

}

可以看到,我们的 blockM 被变成了

((void (*)())&__MBlockObj__testMBlock_block_impl_0((void *)__MBlockObj__testMBlock_block_func_0, &__MBlockObj__testMBlock_block_desc_0_DATA));

__MBlockObj__testMBlock_block_impl_0 需要两个参数, __MBlockObj__testMBlock_block_func_0 主要是对方法的实现内容,第二个参数是对block的一些信息描述。

附:测试代码中用OC对象测试,结果也是相似。

 

2.栈block

测试代码

- (void)testMBlock {
    
    int i = 0;
    
    void (^blockM)(void) = ^{
        
        printf("%d", i);
        
    };
    blockM();
    
}

clang出来的代码是这样的

struct __MBlockObj__testMBlock_block_impl_0 {
  struct __block_impl impl;
  struct __MBlockObj__testMBlock_block_desc_0* Desc;
  int i;
  __MBlockObj__testMBlock_block_impl_0(void *fp, struct __MBlockObj__testMBlock_block_desc_0 *desc, int _i, int flags=0) : i(_i) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __MBlockObj__testMBlock_block_func_0(struct __MBlockObj__testMBlock_block_impl_0 *__cself)
{
  int i = __cself->i; // bound by copy

    printf("%d", i);

}

static struct __MBlockObj__testMBlock_block_desc_0 {
  size_t reserved;
  size_t Block_size;
} __MBlockObj__testMBlock_block_desc_0_DATA = { 0, sizeof(struct __MBlockObj__testMBlock_block_impl_0)};

static void _I_MBlockObj_testMBlock(MBlockObj * self, SEL _cmd) {

    int i = 0;

    void (*blockM)(void) = ((void (*)())&__MBlockObj__testMBlock_block_impl_0((void *)__MBlockObj__testMBlock_block_func_0, &__MBlockObj__testMBlock_block_desc_0_DATA, i));
    ((void (*)(__block_impl *))((__block_impl *)blockM)->FuncPtr)((__block_impl *)blockM);

}

由于访问临时变量 i,于是结构体发生变化,多了一个对应的成员变量,而测试代码执行的任务是打印 i,而 __MBlockObj__testMBlock_block_func_0 中 printf 打印的 i,其实是做了一次值传递。

附:如果把临时变量换成OC对象,比如NSArray的话,clang出来的代码又有变化

struct __MBlockObj__testMBlock_block_impl_0 {
  struct __block_impl impl;
  struct __MBlockObj__testMBlock_block_desc_0* Desc;
  NSArray *mArr;
  __MBlockObj__testMBlock_block_impl_0(void *fp, struct __MBlockObj__testMBlock_block_desc_0 *desc, NSArray *_mArr, int flags=0) : mArr(_mArr) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __MBlockObj__testMBlock_block_func_0(struct __MBlockObj__testMBlock_block_impl_0 *__cself)
{
  NSArray *mArr = __cself->mArr; // bound by copy

        NSLog((NSString *)&__NSConstantStringImpl__var_folders_8x_br8kd9yd70g5q66ssb9jhblh0000gn_T_MBlockObj_6efbe8_mi_0, mArr);
    
}
static void __MBlockObj__testMBlock_block_copy_0(struct __MBlockObj__testMBlock_block_impl_0*dst, struct __MBlockObj__testMBlock_block_impl_0*src)
{
    _Block_object_assign((void*)&dst->mArr, (void*)src->mArr, 3/*BLOCK_FIELD_IS_OBJECT*/);
}

static void __MBlockObj__testMBlock_block_dispose_0(struct __MBlockObj__testMBlock_block_impl_0*src)
{
    _Block_object_dispose((void*)src->mArr, 3/*BLOCK_FIELD_IS_OBJECT*/);
}

static struct __MBlockObj__testMBlock_block_desc_0 {
  size_t reserved;
  size_t Block_size;
  void (*copy)(struct __MBlockObj__testMBlock_block_impl_0*, struct __MBlockObj__testMBlock_block_impl_0*);
  void (*dispose)(struct __MBlockObj__testMBlock_block_impl_0*);
} __MBlockObj__testMBlock_block_desc_0_DATA = { 0, sizeof(struct __MBlockObj__testMBlock_block_impl_0), __MBlockObj__testMBlock_block_copy_0, __MBlockObj__testMBlock_block_dispose_0};

static void _I_MBlockObj_testMBlock(MBlockObj * self, SEL _cmd) {

    NSArray* mArr = ((NSArray *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSArray"), sel_registerName("array"));

    void (*blockM)(void) = ((void (*)())&__MBlockObj__testMBlock_block_impl_0((void *)__MBlockObj__testMBlock_block_func_0, &__MBlockObj__testMBlock_block_desc_0_DATA, mArr, 570425344));
    ((void (*)(__block_impl *))((__block_impl *)blockM)->FuncPtr)((__block_impl *)blockM);

}

这里主要是多了 __MBlockObj__testMBlock_block_copy_0 和 __MBlockObj__testMBlock_block_dispose_0 两个方法。

前者的方法名了有个 copy ,但不要以为是执行了 copy 一类的操作。这个方法里面的具体实现,是调用了 _Block_object_assign 方法,_Block_object_assign 方法会根据传进来的实际参数,判断是block对象(flags对应是7),还是OC对象(flags对应是3),还是引用(flags对应是8)。如果是OC对象,那么会进行retain,然后再赋值。

后者的方法是一个析构函数作用,但也同样判断实际参数类型,如果是OC对象,会进行release。

 

3.堆block

测试代码

@interface MBlockObj : NSObject

@property (nonatomic, strong) NSArray* mArr;

@end

#import "MBlockObj.h"

@implementation MBlockObj

- (void)testMBlock {
    
    _mArr = [NSArray array];
    
    void (^blockM)(void) = ^{
        
        NSLog(@"mArr %@", _mArr);
        
    };
    blockM();
    
}

@end

clang出来的代码

struct __MBlockObj__testMBlock_block_impl_0 {
  struct __block_impl impl;
  struct __MBlockObj__testMBlock_block_desc_0* Desc;
  MBlockObj *self;
  __MBlockObj__testMBlock_block_impl_0(void *fp, struct __MBlockObj__testMBlock_block_desc_0 *desc, MBlockObj *_self, int flags=0) : self(_self) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __MBlockObj__testMBlock_block_func_0(struct __MBlockObj__testMBlock_block_impl_0 *__cself)
{
  MBlockObj *self = __cself->self; // bound by copy

    NSLog((NSString *)&__NSConstantStringImpl__var_folders_8x_br8kd9yd70g5q66ssb9jhblh0000gn_T_MBlockObj_58d1bf_mi_0, (*(NSArray **)((char *)self + OBJC_IVAR_$_MBlockObj$_mArr)));

}
static void __MBlockObj__testMBlock_block_copy_0(struct __MBlockObj__testMBlock_block_impl_0*dst, struct __MBlockObj__testMBlock_block_impl_0*src)
{
    _Block_object_assign((void*)&dst->self, (void*)src->self, 3/*BLOCK_FIELD_IS_OBJECT*/);
}

static void __MBlockObj__testMBlock_block_dispose_0(struct __MBlockObj__testMBlock_block_impl_0*src)
{
    _Block_object_dispose((void*)src->self, 3/*BLOCK_FIELD_IS_OBJECT*/);
}

static struct __MBlockObj__testMBlock_block_desc_0 {
  size_t reserved;
  size_t Block_size;
  void (*copy)(struct __MBlockObj__testMBlock_block_impl_0*, struct __MBlockObj__testMBlock_block_impl_0*);
  void (*dispose)(struct __MBlockObj__testMBlock_block_impl_0*);
} __MBlockObj__testMBlock_block_desc_0_DATA = { 0, sizeof(struct __MBlockObj__testMBlock_block_impl_0), __MBlockObj__testMBlock_block_copy_0, __MBlockObj__testMBlock_block_dispose_0};

static void _I_MBlockObj_testMBlock(MBlockObj * self, SEL _cmd) {

    (*(NSArray **)((char *)self + OBJC_IVAR_$_MBlockObj$_mArr)) = ((NSArray *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSArray"), sel_registerName("array"));

    void (*blockM)(void) = ((void (*)())&__MBlockObj__testMBlock_block_impl_0((void *)__MBlockObj__testMBlock_block_func_0, &__MBlockObj__testMBlock_block_desc_0_DATA, self, 570425344));
    ((void (*)(__block_impl *))((__block_impl *)blockM)->FuncPtr)((__block_impl *)blockM);

}

好吧,上面的代码看出来了,即使我们是访问成员变量,也是经由self去获取到成员变量去访问的,而上面的已经解释过, _Block_object_assign 会retain实际参数,就是说会retain self,在某些情况下会造成循环引用。

 

附1:__block是做了什么操作?

各位可以做测试代码,然后clang一下,可以看到 __block 所修饰的变量,会转成一个结构体,具体的作用,是将这个变量从栈上复制到堆上。

附2:为什么类的属性或成员变量可以直接修改,而临时变量需要声明为 __block 才可以修改?

附1的问题的解释里,临时变量在声明为 __block 后,从栈复制到堆上,而类的属性或成员变量,本身就是在堆上,或者可以理解为各自的作用域不同。

附3:这是block实现过程的源码  https://github.com/mackyle/blocksruntime

附4:block 调用析构函数,释放其所持有的变量的时机?

block 调用析构函数,释放其所持有的变量,是在 block 被设置为 nil,或者被 release 的时候。所以如果是一个临时的block(测试代码中所看到的 block),会在方法调用结束的时候,把block及其持有变量进行释放;而 block 作为属性,除非主动置nil,否则会在其所在类调用 dealloc 的时候释放。

附5:block属性常用copy修饰,其实使用strong也是一样的。

转载于:https://my.oschina.net/u/574245/blog/657705

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值