一文彻底理解iOS block的知识点

可以带着以下问题来阅读本文

基础问题

  1. 什么是 Block?请举例说明 Block 的使用场景。
  2. Block 如何捕获外部变量?请解释 Block 捕获变量的规则。
  3. 请解释 Block 的内存管理,特别是在非 ARC 和 ARC 环境下的区别。

进阶问题

  1. __block 修饰符有什么作用?使用它有什么注意事项?
  2. 请解释 Block 的三种类型及其特点。

高级问题

  1. 请解释 Block 的底层实现原理,包括如何从栈复制到堆。
  2. __forwarding 指针的作用是什么?它如何支持 __block 变量的内存管理?
  3. 为什么要设计__block,__frowarding,其原理是什么?

1. 什么是block?什么是Block调用?

  • Block是将函数及其上下文封装起来的对象。Block调用既是函数调用。

2. Block 的内存结构

Block 在底层是一个 Objective-C 对象,其内存结构包含以下主要部分:

  1. Block 对象头部

    • isa 指针:指向 Block 类的指针,表明这是一个对象。
    • flags:用于表示 Block 的一些属性,如是否已经被复制到堆上、是否有捕获的外部变量等。
    • reserved:保留字段,目前未使用。
    • invoke:函数指针,指向 Block 的执行代码,即 Block 体的实现。
  2. Block 描述信息descriptor):

    • size:Block 结构体的大小。
    • copy_helperdispose_helper:当 Block 从栈复制到堆时,这两个函数指针用于管理捕获的外部变量的内存(如果有的话)。
    • signature:Block 的签名信息,用于描述 Block 的参数和返回值类型(可选)。
  3. 捕获的外部变量

    • Block 会捕获其定义时所在作用域的局部变量。这些变量的副本或引用会紧随 Block 对象头部和描述信息之后存储。
  4. 内存结构示例

假设有如下的 Block 定义:

objective-c 复制代码
int a = 10;
void (^myBlock)(void) = ^{
    NSLog(@"Value of a: %d", a);
};

其内存结构大致如下:

diff 复制代码
+----------------------+
|       isa 指针       |
+----------------------+
|       flags         |
+----------------------+
|      reserved       |
+----------------------+
|      invoke         |
+----------------------+
|    descriptor       |
|      - size         |
|      - copy_helper  |
|      - dispose_helper|
|      - signature    |
+----------------------+
|  捕获的外部变量 a   |
+----------------------+

3. block的内存管理

1. Block 的三种类型

Block 在内存中有三种类型,它们的内存管理方式不同:

  • _NSConcreteGlobalBlock:全局 Block,存储在全局数据区,不需要手动管理内存。
  • _NSConcreteStackBlock:栈 Block,存储在栈上,当离开定义它的作用域时会被自动销毁。
  • _NSConcreteMallocBlock:堆 Block,存储在堆上,需要手动管理内存(在非 ARC 环境下)。

当 Block 发生拷贝时,不同类型的 Block 会有不同的行为和变化:

  1. 全局 Block(_NSConcreteGlobalBlock)
  • 全局 Block 存储在全局数据区,不捕获任何外部变量。
  • 当全局 Block 被复制时,实际上并不会发生真正的内存拷贝操作,而是简单地返回 Block 本身的指针。因此,全局 Block 的拷贝不会引起任何内存或状态的变化。
  1. 栈 Block(_NSConcreteStackBlock)
  • 栈 Block 存储在栈上,它可能捕获外部变量。
  • 当栈 Block 被复制到堆上时(例如,通过 Block_copy 函数或在 ARC 下赋值给强引用),会发生以下变化:
    • 为 Block 分配堆内存,并将栈 Block 的内容(包括捕获的外部变量)复制到堆上的新位置。
    • 如果 Block 捕获了 __block 变量,这些变量也会被一起复制到堆上,并且它们的 __forwarding 指针会被更新,以确保后续对这些变量的访问和修改都指向堆上的副本。
    • 复制后的 Block 成为堆 Block(_NSConcreteMallocBlock),其生命周期由引用计数管理。
  1. 堆 Block(_NSConcreteMallocBlock)
  • 堆 Block 存储在堆上,它的生命周期由引用计数管理。
  • 当堆 Block 被复制时,在 ARC 环境下,引用计数会增加,但不会发生实际的内存拷贝操作。在非 ARC 环境下,需要手动管理 Block 的引用计数和内存释放。

总结:

  • 当 Block 发生拷贝时,全局 Block 不会发生变化,栈 Block 会被复制到堆上,并成为堆 Block,而堆 Block 的引用计数会增加。

4. block捕获变量

在 Objective-C 中,Block 可以捕获其定义时所在作用域的外部变量。这个特性使得 Block 能够访问并使用在其外部定义的变量。以下是 Block 捕获外部变量的一些细节:

捕获方式

  1. 基本数据类型变量:对于基本数据类型(如 int、float 等)的局部变量,Block 会捕获其值的副本。这意味着在 Block 内部对这些变量的修改不会影响原始变量的值。

    objective-c 复制代码
    int value = 10;
    void (^myBlock)(void) = ^{
        NSLog(@"Value inside block: %d", value);
    };
    value = 20;
    myBlock(); // 输出 "Value inside block: 10"

    在这个例子中,value 在 Block 内部的值仍然是 10,即使在 Block 外部 value 被修改为 20。

  2. 对象类型变量:对于对象类型的局部变量,Block 会捕获对对象的强引用。这意味着在 Block 内部可以访问和修改对象的属性,这些修改会反映到原始对象上。

    objective-c 复制代码
    NSMutableArray *array = [NSMutableArray arrayWithObjects:@"a", @"b", nil];
    void (^myBlock)(void) = ^{
        [array addObject:@"c"];
    };
    myBlock();
    NSLog(@"Array: %@", array); // 输出 "Array: (a, b, c)"

    在这个例子中,array 在 Block 内部被修改,添加了一个新元素 "c"。

总结

Block 捕获外部变量的能力使得它们非常灵活和强大,特别是在异步编程和回调中。正确理解 Block 如何捕获和使用外部变量对于编写正确和高效的代码非常重要。

5. block如何修改外部变量值:__block

__block的示例

如果你需要在 Block 内部修改一个基本数据类型的局部变量的值,你可以使用 __block 修饰符。这样,Block 会通过引用而不是值来捕获这个变量,从而允许在 Block 内部对变量进行修改。

objective-c 复制代码
__block int value = 10;
void (^myBlock)(void) = ^{
    value = 20; // 修改 __block 变量的值
};
myBlock();
NSLog(@"Value: %d", value); // 输出 "Value: 20"

在这个例子中,使用 __block 修饰符允许 Block 修改 value 的值。

__block 是什么?

在 Objective-C 中,__block 是一个存储类型修饰符,用于修饰在 Block 中使用的变量。当一个变量被 __block 修饰时,它允许在 Block 内部对该变量进行修改。

原理

  1. 封装成结构体 :使用 __block 修饰的变量会被编译器封装成一个结构体。这个结构体包含变量的值和一个 __forwarding 指针,用于支持变量从栈复制到堆的过程。
  2. 引用捕获 :与普通局部变量通过值捕获不同,__block 变量是通过引用捕获的。这意味着 Block 内部访问的是 __block 变量的地址,而不是它的副本。
  3. 栈到堆的复制 :当 Block 被复制到堆上时(例如,赋值给强引用变量或作为函数返回值),所有被 Block 捕获的 __block 变量也会被复制到堆上。复制过程中,__forwarding 指针会被更新,指向堆上的变量副本。

为什么需要 __block?

  1. 修改外部变量 :在没有 __block 修饰符的情况下,Block 只能捕获外部变量的值,而不能修改这些变量。__block 允许在 Block 内部修改外部变量的值。
  2. 支持变量的生命周期 :当 Block 被复制到堆上时,__block 变量也会被复制到堆上,以确保在 Block 的生命周期内,变量仍然有效。
  3. 简化内存管理 :在自动引用计数(ARC)环境下,__block 变量的内存管理会被自动处理,包括当变量被复制到堆上时的内存管理。
  4. 适应异步编程模式 :在异步编程中,经常需要在 Block 中修改外部变量以存储异步操作的结果。__block 使得这种模式更加容易实现。

总的来说,__block 修饰符是为了增强 Block 的功能,使其能够修改捕获的外部变量。这在异步编程、回调处理以及其他需要在 Block 中修改外部状态的场景中非常有用。了解 __block 的原理和作用有助于更有效地使用 Block 和管理内存。

6. __forwarding

__forwarding 是一个在 Objective-C 中与 __block 变量相关的机制。它是 __block 变量结构体中的一个指针,用于确保无论变量是在栈上还是堆上,对该变量的访问和修改都能正确进行。

原理

  1. __block 变量的封装 :使用 __block 修饰符声明的变量会被编译器封装成一个结构体,该结构体包含变量的值和一个 __forwarding 指针。
  2. 指针指向自身 :初始时,__block 变量的 __forwarding 指针指向变量自身(即指向结构体本身)。
  3. 复制到堆上 :当 Block 被复制到堆上时,所有被 Block 捕获的 __block 变量也会被复制到堆上。此时,__forwarding 指针会被更新,指向堆上的变量副本。
  4. 统一访问方式 :无论 __block 变量是在栈上还是堆上,对它的访问和修改都通过 __forwarding 指针进行。这确保了变量的访问和修改总是指向正确的存储位置。

为什么需要 __forwarding

  1. 保持访问一致性__forwarding 机制确保在 Block 中对 __block 变量的访问和修改总是一致的,无论变量是在栈上还是堆上。
  2. 支持栈到堆的迁移 :当 Block 和 __block 变量从栈复制到堆时,__forwarding 指针使得变量的迁移过程更加平滑。它保持了对变量的引用不变,即使变量的存储位置发生了变化。
  3. 简化内存管理__forwarding 机制简化了 __block 变量在栈和堆之间迁移时的内存管理。开发者不需要关心变量的具体存储位置,只需通过 __forwarding 指针进行访问和修改。

总的来说,__forwarding__block 变量机制的一个重要组成部分,它确保了在 Block 中使用 __block 变量时的访问一致性和内存管理的简化。了解 __forwarding 的原理和作用有助于更深入地理解 Block 和 __block 变量的工作机制。

总结

__block 修饰符的原理涉及到 __block 变量的存储方式、捕获机制和内部结构。通过将变量封装为结构体并通过引用捕获,__block 允许在 Block 中修改外部变量的值。在 Block 被复制到堆上时,__forwarding 指针确保对变量的访问和修改都通过同一个地址进行,确保了数据的一致性和正确性。

相关推荐
若水无华1 天前
fiddler 配置ios手机代理调试
ios·智能手机·fiddler
Aress"1 天前
【ios越狱包安装失败?uniapp导出ipa文件如何安装到苹果手机】苹果IOS直接安装IPA文件
ios·uni-app·ipa安装
Jouzzy2 天前
【iOS安全】Dopamine越狱 iPhone X iOS 16.6 (20G75) | 解决Jailbreak failed with error
安全·ios·iphone
瓜子三百克2 天前
采用sherpa-onnx 实现 ios语音唤起的调研
macos·ios·cocoa
左钦杨2 天前
IOS CSS3 right transformX 动画卡顿 回弹
前端·ios·css3
努力成为包租婆2 天前
SDK does not contain ‘libarclite‘ at the path
ios
安和昂2 天前
【iOS】Tagged Pointer
macos·ios·cocoa
I烟雨云渊T3 天前
iOS 阅后即焚功能的实现
macos·ios·cocoa
struggle20253 天前
适用于 iOS 的 开源Ultralytics YOLO:应用程序和 Swift 软件包,用于在您自己的 iOS 应用程序中运行 YOLO
yolo·ios·开源·app·swift
Unlimitedz3 天前
iOS视频编码详细步骤(视频编码器,基于 VideoToolbox,支持硬件编码 H264/H265)
ios·音视频