Block基础

Block基础

文章目录

  • Block基础
    • [一、先搞懂:Block 到底是什么?](#一、先搞懂:Block 到底是什么?)
    • [二、Block 的标准语法](#二、Block 的标准语法)
    • [三、Block 最核心能力:捕获外部变量](#三、Block 最核心能力:捕获外部变量)
    • [四、Block 常见使用场景(开发必用)](#四、Block 常见使用场景(开发必用))
      • [场景 1:代码异步执行(网络请求、延时操作)](#场景 1:代码异步执行(网络请求、延时操作))
      • [场景 2:回调传值(A 页面 → B 页面,B 做完通知 A)](#场景 2:回调传值(A 页面 → B 页面,B 做完通知 A))
    • [五、Block 的内存问题(循环引用)](#五、Block 的内存问题(循环引用))
      • [1. 什么是循环引用?](#1. 什么是循环引用?)
      • [2. 解决方案:weakSelf 弱引用](#2. 解决方案:weakSelf 弱引用)
      • [3.防止 Block 执行中 self 被销毁(strongSelf)](#3.防止 Block 执行中 self 被销毁(strongSelf))
    • [六、Block 类型](#六、Block 类型)
    • 七、总结

一、先搞懂:Block 到底是什么?

Block 就是一段 "可以打包保存、随时调用" 的代码块

  • 它像一个匿名函数(没有名字的函数)
  • 它能捕获外部变量(这是它最强大的地方)
  • 它可以当作参数传递当作变量存储

Block 和普通函数的区别(对比记忆)

对比维度 普通函数 Block
是否有名字 有(如 - (void)sayHello;) 无(匿名)
能否捕获外部局部变量 不能(只能通过参数传递) 能(默认只读,加 __block 可修改)
能否当作变量存储 不能 能(存在栈/堆/全局区)
能否当作参数传递 不能(只能传递函数指针) 能(直接传递 Block 变量)

二、Block 的标准语法

完整格式

复制代码
返回值类型 (^block变量名)(参数列表) = ^返回值类型(参数列表) {
    // 代码块
};
  1. 无返回值、无参数
objc 复制代码
void (^myBlock)(void) = ^{
  NSLog(@"我是一个最简单的Block");
}
  1. 有参数、无返回值
objc 复制代码
// 定义:接收一个字符串参数
void (^sayHello)(NSString *) = ^(NSString *name){
    NSLog(@"你好,%@", name);
};

// 调用
sayHello(@"小明");
  1. 有参数、有返回值
objc 复制代码
// 定义:计算两个数的和
int (^sumBlock)(int, int) = ^(int a, int b){
    return a + b;
};

// 调用
int result = sumBlock(10, 20);
NSLog(@"结果:%d", result); // 输出 30

三、Block 最核心能力:捕获外部变量

捕获的变量类型及规则(分3类,清晰区分)

类别1:局部变量(最常用,重点掌握)

局部变量:定义在函数/方法内部的变量(比如在 main 函数里定义的 int age = 18),Block 对其捕获规则如下:

  • 默认:只能"读取",不能"修改"(Block 会捕获变量的"副本",而非变量本身)。

  • 想修改:必须在变量前面加 __block 关键字(双下划线+block),此时 Block 捕获的是变量的"地址",而非副本,就能正常修改。

objc 复制代码
// 示例1:未加 __block,只能读取,不能修改
int age = 18; // 局部变量
void (^readBlock)(void) = ^{
    NSLog(@"年龄(读取):%d", age); //正常读取,输出 18
    // age = 20; //报错:Variable is not assignable (missing __block type specifier)
};
readBlock();

// 示例2:加 __block,可修改
__block int height = 170; // 加 __block 关键字
void (^changeBlock)(void) = ^{
    height = 180; //正常修改
    NSLog(@"身高(修改后):%d", height); // 输出 180
};
changeBlock();
类别2:全局变量(无需捕获,直接访问)

全局变量:定义在函数/方法外部的变量(比如在 main 函数外面定义的 int globalNum = 100),Block 无需捕获,可直接读取、修改,无需加 __block。

objc 复制代码
// 全局变量(定义在 main 函数外面)
int globalNum = 100;

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        void (^globalBlock)(void) = ^{
            globalNum = 200; // 直接修改,无需 __block
            NSLog(@"全局变量修改后:%d", globalNum); // 输出 200
        };
        globalBlock();
    }
    return 0;
}
类别3:静态变量(static,可直接修改,无需 __block)

静态变量:用 static 修饰的局部变量(比如 static int staticNum = 50),Block 会捕获它的"地址",因此可直接修改,无需加 __block。

objc 复制代码
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        static int staticNum = 50; // 静态局部变量
        void (^staticBlock)(void) = ^{
            staticNum = 150; // 直接修改,无需 __block
            NSLog(@"静态变量修改后:%d", staticNum); // 输出 150
        };
        staticBlock();
    }
    return 0;
}

四、Block 常见使用场景(开发必用)

场景 1:代码异步执行(网络请求、延时操作)

objc 复制代码
// 延时 2 秒执行一段代码
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    NSLog(@"2 秒后执行");
});

场景 2:回调传值(A 页面 → B 页面,B 做完通知 A)

比如:第二个页面选择完数据,通知第一个页面

objc 复制代码
// B 页面.h 文件
@property (nonatomic, copy) void(^selectResult)(NSString *data);

// B 页面.m 选择完调用
self.selectResult(@"我是选择的数据");

// A 页面使用
BViewController *bVC = [[BViewController alloc] init];
bVC.selectResult = ^(NSString *data){
    NSLog(@"收到 B 页面的数据:%@", data);
};

五、Block 的内存问题(循环引用)

1. 什么是循环引用?

简单说:

  • 对象 A 强引用 Block

  • Block 内部强引用了对象 A

    → 两者互相抓住不放,

    谁都不释放,造成内存泄漏。

2. 解决方案:weakSelf 弱引用

objc 复制代码
// 第一步:定义弱引用 self
__weak typeof(self) weakSelf = self;

// 第二步:Block 内部只用 weakSelf,不用 self
self.myBlock = ^{
    // 安全,不会循环引用
    NSLog(@"%@", weakSelf.name);
};

3.防止 Block 执行中 self 被销毁(strongSelf)

block执行流程:

  1. 页面 / 控制器 self 已经提前退出、销毁(比如用户退出当前页面)

  2. 但异步 Block 还没来得及执行,还在排队

  3. 等轮到 Block 执行时:原来的 self 早就被系统回收销毁了

objc 复制代码
// 1. 外层弱引用:打破循环引用,允许页面正常销毁
__weak typeof(self) weakSelf = self;
self.myBlock = ^{
    //2.内部再强引用一次,保证执行代码时 self 还在
    __strong typeof(weakSelf) strongSelf = weakSelf;
    // 判空:防止页面早就销毁、weakSelf 已经是 nil
    if (strongSelf) {
        strongSelf.name = @"测试";
    }
};

六、Block 类型

Block 存放在内存 3 个地方,开发不用手动管

  1. NSGlobalBlock(全局区):没访问外部变量
  2. NSStackBlock(栈区):访问了变量,MRC 用
  3. NSMallocBlock (堆区):copy 后,ARC 下最常用

七、总结

  1. Block = 可保存、可调用、可传参的代码段
  2. 语法记住:返回值 (^名称)(参数) = ^{}
  3. 修改变量用 __block
  4. 内部用 self 必须 __weak 防循环引用
  5. 实际开发主要用于回调、异步、遍历
相关推荐
枫叶丹41 小时前
【HarmonyOS 6.0】AVSession Kit 新增私有数据发送能力详解
开发语言·华为·harmonyos
skilllite作者1 小时前
从“记忆”到“项目 Wiki”:我在 SkillLite 里实现了一套 Markdown-only LLM Wiki 自动维护机制
开发语言·jvm·人工智能·后端·架构·rust
㳺三才人子1 小时前
簡介 python 文字轉語音
开发语言·python
炘爚1 小时前
C++(整理合集)
开发语言·c++
buhuizhiyuci1 小时前
[QT]QT入门的项目创建和项目代码的介绍
开发语言·qt
Brilliantwxx1 小时前
【C++】认识标准库STL(1)
开发语言·c++·笔记·程序人生·算法
XMYX-01 小时前
20 - Go 互斥锁:Mutex 与并发安全
开发语言·golang
郝学胜-神的一滴2 小时前
深入epoll封装:event_set与event_add核心原理剖析
linux·服务器·开发语言·网络·c++·unix
gCode Teacher 格码致知2 小时前
Javascript提高:国际化 API(Intl 对象)详解-由Deepseek产生
开发语言·javascript·ecmascript