iOS面试知识整理 - Block

148 阅读3分钟

Block

Block介绍

Block是将函数及其执行上下文封装起来的对象

什么是Block调用

本质是函数调用

截获变量

  • 局部变量

    • 基本数据类型 -> 截获其值
    • 对象类型 -> 连同对象所有权修饰符一起截获
  • 静态局部变量 -> 以指针形式截获

  • 全局变量 -> 不截获

  • 静态全局变量 -> 不截获

__block修饰符

一般情况下,对被截获变量进行赋值操作需添加__block操作符

局部变量(无论是基本数据类型还是对象)需要赋值的时候要用到__block

静态局部变量、全局变量和静态全局变量就不需要

__block修饰的变量变成了对象

栈上的block的__forwarding指针是指向本身的

Block的内存管理

block的分类

  • 全局Block - Global .data区

    • copy - 什么都不做
  • 栈上Block - Stack

    • 增加引用计算
  • 堆上Block Malloc

    • copy到堆上

__forwarding的总结

  • block只在栈上

    • __forwarding指针指向栈上block本身
  • block被Copy到堆上

    • 栈上的__forwarding指针指向堆上block的copy
    • 堆上的__forwarding指针指向堆上block本身

不论在任何内存,都可以顺利的访问一个__block变量

Block的循环引用

__weak可以解除自循环引用

block内部使用__block

  • 在MRC下,不会产生循环引用

  • 在ARC下,会产生循环引用,会引起内存泄漏,是大环引用

    • 对象持有block,block持有block变量, __ block持有对象,就会变成大环引用,无法释放

面试总结

什么是block?

Block是将函数及其执行上下文封装起来的对象

为什么Block会产生循环引用?

  1. 连同对象所有权修饰符一起截获,如果self对对象也是强引用,那就会出现自循环引用
  2. __block也会导致循环引用

怎么理解Block截获变量的特性?

  • 局部变量

    • 基本数据类型 -> 截获其值
    • 对象类型 -> 连同对象所有权修饰符一起截获
  • 静态局部变量 -> 以指针形式截获

  • 全局变量 -> 不截获

  • 静态全局变量 -> 不截获

Block的类型?

  1. 初始化刚刚结束的时候,block会被放到数据区。
  2. 在block内部访问外界变量,block会被转移到栈空间存储。
  3. 在ARC环境下,访问外界变量的block会先被转移到栈空间,然后copy到堆空间。

Block复制拷贝到堆的时机

  1. 为什么要复制,由于Block是默认建立在栈上, 所以如果离开方法作用域, Block就会被丢弃
  2. 调用block的copy函数时;
  3. Block作为函数返回值返回时;
  4. 将Block赋值给附有__strong修饰符id类型的类或者Block类型成员变量时;
  5. 方法中含有usingBlock的Cocoa框架方法或者GCD的API中传递Block时;
  6. 这些情况下,可归结为_Block_copy函数被调用时Block从栈复制到堆。释放Block时调用其dispose函数,相当于对象的dealloc实例方法。

在 ARC 中,捕获外部了变量的 block 的类会是 NSMallocBlock 或者 NSStackBlock,如果 block 被赋值给了某个变量在这个过程中会执行 _Block_copy 将原有的 NSStackBlock 变成 NSMallocBlock

但是如果 block 没有被赋值给某个变量,那它的类型就是 NSStackBlock

没有捕获外部变量的 block 的类会是 NSGlobalBlock 即不在堆上,也不在栈上,它类似 C 语言函数一样会在代码段中。