天天看点

block相关知识点

block存储区域

首先我们得明白iOS系统存储有哪几大内存区域:

1、栈区

    由编译器自动分配并释放,存放函数的参数值(实参),局部变量等。栈是向低地址扩展的数据结构,是不连续的内存区域,采用后进先出(LIFO )。优点是快速高效,缺点时有限制,数据不灵活。

2、堆区

    由程序员分配和释放,如果程序员不释放,程序结束时,可能会由操作系统回收。堆是向高地址扩展的数据结构,是不连续的内存区域,以链表的方式进行存储。

3、全局静态区

    全局区分为初始化全局区(bbs)和未初始化全局区(data)。全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域,在程序结束后有系统释放。

4、常量区

    存储常量数据,通常程序结束后由系统自动释放。

5、代码区

    用来存放函数的二进制代码,在运行时要防止被非法修改,只允许读取不允许操作。

下面看一段示例

block相关知识点
block相关知识点

block是一个匿名函数,具体自动捕获外部变量的能力

从上面的示例我们可以发现:

一个普通的不包含任何外部变量的block(比如示例中的1、4、7)是存储在全局静态区的,不管这个block作为临时变量还是属性或者其他方式;

单纯作为block存在,但是有用到外部变量的(比如示例中的1、2、3)存储在栈区,因为这样可以更快的访问外部变量;

如果block用=复制给了一个变量,同时block获取了外部变量(比如示例中的5、6、8、9)存储在堆区,因为相当于从栈区拷贝到堆区。ARC情况下,可以用copy、strong修饰block,只是为了习惯,基本都是用的copy。

block循环引用产生情况及解决方法

1、最常见的情况,循环链self -> block -> self

block相关知识点

解决方法:

(1)用__weak修饰self

block相关知识点

(2)用一个中间临时变量保存self

block相关知识点

(3)将self作为参数传进block

block相关知识点

2、单纯用__weak修饰self有时也会产生一个新的问题

block相关知识点
block相关知识点

因为在 block里面用到self的属性时,self已经被释放,self = nil,所以此时拿到的属性值为空。

解决方法:__strong再次强引用一遍

block相关知识点

下面再看看 block到底做了什么,或者说block是怎么实现的?

现在main.m有这么一段代码

block相关知识点

在终端执行clang -rewrite-objc main.m得到main.cpp

block相关知识点

void (^block)(void) = ^{

            NSLog(@"这是一个block");

        };

 block();

反编译之后,再简化

block = __main_block_impl_0(__main_block_func_0, __main_block_desc_0_DATA);

block->FuncPtr(block);

block是__main_block_impl_0

block->FuncPtr = fp =  __main_block_func_0 =  ^{  NSLog(@"这是一个block");  };

最后看__block修饰是如何修改外部变量

main.m有这么一段代码

block相关知识点

在终端执行clang -rewrite-objc main.m得到main.cpp

block相关知识点

 __block int a = 1;

 void (^block)(void) = ^{

        a++;

   };

  block();

反编译再简化

a = {0, &a, 0, sizeof(__Block_byref_a_0), 1};

block = __main_block_impl_0(__main_block_func_0, __main_block_desc_0_DATA, (__Block_byref_a_0 *)&a, flag);

block->FuncPtr(block)

static void __main_block_func_0(struct __main_block_impl_0 *__cself) {

  __Block_byref_a_0 *a = __cself->a; // bound by ref     // 现在用的是a的地址,所以block里面修改a的值,外面会有变化

            (a->__forwarding->a)++;

        }