iOS学习开发

关于解决block循环引用的一点思考

2018-03-09  本文已影响9人  牛顿爱编程

导语:使用weakSelf,strongSelf可以解决循环引用原理分析


在使用block过程中经常会出现循环引用的情况:

比如下面的代码:

  self.block = ^() {
          [self doSomething];
    };

因为block作为当前对象的一个属性,又强引用了该对象,导致循环引用。

解决方法一般:

 __weak __typeof(self) weakSelf = self;
    self.block = ^() {
        __strong __typeof(weakSelf) strongSelf = weakSelf;
        [strongSelf doSomething];
    };

但是该解决方式在block中又强引用了weakSelf,为什么没有导致循环引用呢?

之前也一直比较疑惑,后来分析了下block的源码,终于搞清楚了。

首先,我们看一下什么是block。

(1)新建一个文件,定义一个block并执行:

#include <stdio.h>

int main(){
    void (^blk)(void) = ^(){printf("Hello world!");};
    blk();
    return 0;
}

对于这个简单的block文件使用 clang -rewrite-objc hello.c 语句可以得到它对应的cpp文件,主要代码有:

struct __block_impl {
  void *isa;
  int Flags;
  int Reserved;
  void *FuncPtr;
};

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
printf("Hello world!");}

static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};
int main(){
    void (*blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
    ((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);
    return 0;
}

首先__block_impl结构体中有四个变量:其中isa表明block也有runntime的特性,isa是指向Class的指针;FuncPtr是函数指针,指向block实际执行的函数。

__main_block_impl_0结构体有两个变量:一个是__block_impl变量;一个是__main_block_desc_0变量,是对__main_block_impl_0的描述。

__main_block_func_0是一个方法,也就是调用block时执行的函数。

再看main()函数,可以看到block实际被转换成一个指向__main_block_impl_0结构体的指针,block()执行过程也就是blk->FuncPtr()的执行过程。
(2)修改一下文件中的代码:

#include <stdio.h>

int main(){
    int i = 0;
    void (^blk)(void) = ^(){printf("i is %d", i);};
    i++;
    blk();
    return 0;
}

转换成cpp文件:

struct __block_impl {
  void *isa;
  int Flags;
  int Reserved;
  void *FuncPtr;
};

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  int i;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _i, int flags=0) : i(_i) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  int i = __cself->i; // bound by copy
printf("i is %d", i);}

static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};

int main(){
    int i = 0;
    void (*blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, i));
    i++;
    ((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);
    return 0;
}

和之前的代码比较可以发现,在__main_block_impl_0结构体中多了一个变量i,并且在创建block的时候i被赋值,注意int i = __cself->i; // bound by copy 所以block创建之后再修改i的值对于block的执行结果没有影响。
(3)继续修改:

#include <stdio.h>

int main(){
    int __block i = 0;
    void (^blk)(void) = ^(){printf("i is %d", i);};
    i++;
    blk();
    return 0;
}

cpp文件:

struct __block_impl {
  void *isa;
  int Flags;
  int Reserved;
  void *FuncPtr;
};

struct __Block_byref_i_0 {
  void *__isa;
__Block_byref_i_0 *__forwarding;
 int __flags;
 int __size;
 int i;
};

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  __Block_byref_i_0 *i; // by ref
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_i_0 *_i, int flags=0) : i(_i->__forwarding) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  __Block_byref_i_0 *i = __cself->i; // bound by ref
printf("i is %d", (i->__forwarding->i));}

static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->i, (void*)src->i, 8/*BLOCK_FIELD_IS_BYREF*/);}

static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->i, 8/*BLOCK_FIELD_IS_BYREF*/);}
static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
  void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
  void (*dispose)(struct __main_block_impl_0*);
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};

int main(){
    __Block_byref_i_0 i = {(void*)0,(__Block_byref_i_0 *)&i, 0, sizeof(__Block_byref_i_0), 0};
    void (*blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_i_0 *)&i, 570425344));
    (i.__forwarding->i)++;
    ((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);
    return 0;
}

该block中包含可以修改的变量,情况相对比较复杂。只看变化比较明显的地方,此时i是通过__Block_byref_i_0 *i; // by ref 引用到外面的i值,也就是外面i值的改变是可以改变block中的i值。更深入的分析由于水平有限暂时不展开了。

所以:

self.block = ^() {
          [self doSomething];
    };
上一篇下一篇

猜你喜欢

热点阅读