Block底层原理

循环引用

对象持有Block, Block 又捕获该对象

__weak不会产生强引用,指向的对象销毁时,会自动将指针置为nil。因此一般通过__weak来解决问题。

__unsafe_unretained不会产生强引用,不安全,指向的对象销毁时,指针存储的地址值不变。

使用__block也可以解决循环引用的问题, 只是在block执行之后才会把__block的结构体置为nil,解除循环引用

捕获对象

Block 会捕获局部变量,不会捕获全局变量,因为全局变量在哪里都可以访问,不需要捕获。

注意:为什么会捕获self
因为无论是对象方法或类方法,都会默认将self作为参数传递到方法内部,作为参数的话就是局部变量,局部变量就会被Block捕获。
访问方式:

  1. auto变量: 值传递 (值捕获)
  2. 局部static变量:指针传递 (地址捕获)
  3. 全局变量:直接访问 (不捕获)
内存区域变化

新创建的Block默认在数据段中,引用局部变量时会捕获变量,且Block会变为栈区;
栈区Block被copy或赋值给Strong变量后会变为堆区Block;
堆区Block被copy后类型不发生改变,引用计数增加

ARC环境下,编译器会根据情况自动将栈区的Block进行一次copy操作,将Block复制到堆上, 以下情况会进行copy操作:
  1. Block作为函数返回值时
  2. 将Block赋值给 __strong指针时
  3. Block作为Cocoa API中方法含有usingBlock的方法参数时
  4. Block作为GCD API的方法参数时
对象类型的引用
  1. Block捕获的变量为对象类型,会生成copy和dispose方法对内部引用的对象进行内存管理
  2. Block在栈上,访问对象类型auto变量时,不会对改变量产生强引用,不论Block结构体内部的变量时__strong修饰还是__weak修饰
  3. 如果Block被拷贝到堆上,copy函数会调用assign函数,根据auto变量的修饰符去形成强引用或弱引用
  4. 如果Block从堆中移除,会调用dispose函数自动释放引用的auto变量
Block内部不能修改局部变量的原因
  1. Block内部是值引用,拿到的只是Block内部的值,因此无法修改外部的变量
  2. 使用static修饰基本数据类型变量可以拿到变量地址,因此可以在Block内部修改外部变量值
  3. __block修饰可以解决Block内部不能修改变量值的问题,不可以用来修饰static变量和全局变量。__block将变量包装成对象,把变量封装在结构体中,Block内部存储的是结构体指针,可以通过指针找到内存地址进而修改变量的值
  4. 仅仅使用内存地址,而不是修改该变量的话,不用添加__block,否则系统会自动创建相应的结构体进行存储,占用不必要的内存

你可能感兴趣的:(Block底层原理)