五个BLOCK问题

block的原理是怎样的?本质是什么?

一个含有自动变量的匿名函数

Block 的本质是一个封装了函数及其调用环境的 Objective-C 对象

一个 block 实际是一个对象,它主要由一个 isa 和 一个 impl 和 一个 descriptor 组成。

是封装了函数代码以及其执行上下文(捕获的变量和常量)的对象

一个 block 实例由 6 部分构成:

  1. isa 指针,所有对象都有该指针,用于实现对象相关的功能。
  2. flags,用于按 bit 位表示一些 block 的附加信息,本文后面介绍 block copy 的实现代码可以看到对该变量的使用。
  3. reserved,保留变量。
  4. invoke,函数指针,指向具体的 block 实现的函数调用地址。
  5. descriptor, 表示该 block 的附加描述信息,主要是 size 大小,以及 copy 和 dispose 函数的指针。
  6. variables,capture 过来的变量,block 能够访问它外部的局部变量,就是因为将这些变量(或变量的地址)复制到了结构体中。

__block的作用是什么?有什么使用注意点?

__block可以用于解决 block 内部无法修改auto变量值,__block不能也没必要修饰全局变量、静态变量

__block 是一个在 Block 内部修饰变量的关键字。使用 __block 修饰的变量可以在 Block 内部被修改,并且这些修改会在 Block 执行后在外部保留。在 Block 内部修改通过 __block 修饰的变量不会创建一个新的拷贝,而是直接修改原始变量。

  • 使用 __block 修饰的变量会增加其在栈上的生命周期,当 Block 在堆上被拷贝时,__block 修饰的变量也会被拷贝到堆上,确保 Block 内部对该变量的修改在 Block 外部也能得到保留。
  • 小心循环引用:如果一个对象持有一个拥有 __block 修饰的 Block 的强引用,而该 Block 又捕获了该对象,可能导致循环引用。此时需要在 Block 内部使用 __weak 来修饰对象,或者使用 weakSelfstrongSelf 来避免循环引用。
  • 在block执行的代码块中,将捕获的变量重置为 nil,缺点是必须执行完 block 块才会解决循环引用
  • 捕获变量作为参数传入

block的属性修饰词为什么是copy?使用block有哪些使用注意?

Block 在定义时通常存储在栈上,但如果你将 Block 赋值给一个引用类型(如将 Block 赋值给对象属性),为了避免在 Block 在栈上销毁后导致访问野指针的问题,需要将其拷贝到堆上。使用 copy 修饰符会在将 Block 赋值给引用类型时执行一次拷贝操作,确保 Block 在堆上得到正确的管理,以便在其生命周期内保持有效。

MRC 下 block 如果没有 copy 到堆上,值捕获不会对外部变量引用。 虽然 ARC 环境 strong 也可以修饰 Block,那是因为编译器会对 strong 修饰的 block 也会进行一次 copy 操作。为什么用 copy 修饰算是历史习惯问题,推荐不管 ARC、MRC 使用 copy 修饰 。使用注意:循环引用问题
block一旦没有进行copy操作,就不会在堆上
ARC 环境下下列操作会自动 block 进行 copy 操作:

  • block 作为方法的返回值
  • 将 block 赋值给 __strong 指针时
  • block 作为Cocoa API中方法名含有usingBlock的方法参数时
  • block 作为GCD API的方法参数时
  • block捕获变量使用的时候

block在修改NSMutableArray,需不需要添加__block?

在 Block 内部修改 NSMutableArray(或其他可变集合类)的内容时,并不需要添加 __block 修饰符。可变集合类是引用类型,所以 Block 内部对它的修改会直接影响到原始对象,无需使用 __block 修饰符。

ARC 对 block 类型的影响

在 ARC 开启的情况下,将只会有 NSConcreteGlobalBlock 和 NSConcreteMallocBlock 类型的 block。

原本的 NSConcreteStackBlock 的 block 会被 NSConcreteMallocBlock 类型的 block 替代。证明方式是以下代码在 XCode 中,会输出 <__NSMallocBlock__: 0x100109960>。在苹果的 官方文档 中也提到,当把栈中的 block 返回时,不需要调用 copy 方法了。

五个BLOCK问题_第1张图片

我个人认为这么做的原因是,由于 ARC 已经能很好地处理对象的生命周期的管理,这样所有对象都放到堆上管理,对于编译器实现来说,会比较方便。

你可能感兴趣的:(ios)