先把4 种易混指针定义、成因、区别、崩溃现象、规避方案一次性讲清,全是 iOS/OC 底层实打实考点。
一、先搞懂基础:指针本质
指针 = 内存地址变量
- 保存的是某块内存的起始地址
- OC 里
id、对象、基本类型指针,全是地址 - 堆、栈、全局区都靠指针访问
内存分三块:
- 栈:系统自动分配、自动回收(函数局部变量)
- 堆:手动 malloc/alloc,自己管理释放
- 全局 / 静态区:程序生命周期一直存在
所有野指针、悬垂指针,根源都是:指针还在,指向的内存已经失效 / 被回收。
二、4 种指针完整定义 + 场景 + 区别
1. 空指针 NULL /nil
定义:指针地址 = 0,不指向任何合法内存。
objc
id obj = nil;
int *p = NULL;
特点:
- 给空指针发消息、调用方法 不会崩溃
- 地址 0 是系统保护地址,不允许读写
- 安全、可控,日常开发大量用
用途:
- 初始化指针、对象置空、防野指针
free后置ptr = NULL
2. 野指针 Wild Pointer(最常见、最容易崩溃)
定义:指针不为空,指向一块未知、未申请、已释放、无权访问的随机内存地址。
核心特征:
- 指针有值,但指向的内存非法
- 访问直接 EXC_BAD_ACCESS 崩溃
- 崩溃随机、偶现、难复现
产生原因:
- 局部对象栈内存出作用域被销毁,指针还保留旧地址
free/release后没置空,指针还指着已释放堆内存- 未初始化指针,指向随机垃圾地址
- 数组越界、内存踩踏破坏指针指向
示例野指针:
objc
Person *p; // 未初始化,随机地址
[p sayHi]; // 野指针,直接崩溃
3. 悬垂指针 Dangling Pointer(常和野指针混淆)
定义:指针本身合法,但指向的内存已经被释放 / 销毁,指针还挂着旧地址,悬空吊着。
一句话区别:
- 野指针:一开始就乱指、随机地址
- 悬垂指针:曾经正常指向,内存释放后变成悬空
产生场景:
- MRC 下
release对象,没置nil - C 语言
free堆内存,指针不置空 - 栈局部变量生命周期结束,外部还持有它的地址
示例悬垂指针:
c
运行
int *fun() {
int a = 10;
return &a; // a 在栈上,函数结束立刻销毁
}
// 拿到的就是 悬垂指针
int *p = fun();
悬垂指针访问,大概率也会野指针崩溃,很多公司面试会刻意区分:
野指针:随机乱指悬垂指针:曾经合法,内存已回收,指针还吊着
4. 僵尸指针 Zombie Pointer
定义:对象已经 dealloc 销毁,但系统不立即回收内存,标记为僵尸对象;指针还指向这块 “尸体内存”,就是僵尸指针。
开启僵尸模式(Xcode Zombie):
- 对象销毁后不真的回收,标记为 NSZombie
- 再访问会直接打印:
message sent to deallocated instance - 专门用来排查野指针 / 悬垂指针崩溃
特点:
- 开发调试用,线上关闭
- 能精准定位:哪个对象、哪个方法被发消息时已销毁
三、野指针 vs 悬垂指针 极简对比
表格
| 类型 | 地址来源 | 内存状态 | 现象 |
|---|---|---|---|
| 空指针 | 地址 = 0 | 无内存 | 发消息不崩溃 |
| 野指针 | 随机垃圾地址 | 非法未知内存 | 必崩 / 随机崩 |
| 悬垂指针 | 曾经合法地址 | 内存已释放回收 | 访问就崩 |
| 僵尸指针 | 原对象地址 | 标记僵尸不回收 | 调试可精准报错 |
记忆口诀:乱指叫野,释放没置空叫悬垂,销毁留尸体叫僵尸。
四、iOS 里为什么野指针崩溃难查?
- 内存释放后不一定立刻被覆盖,有时能正常跑,偶尔崩
- 栈内存复用、堆内存复用,随机性极强
- 多线程时序问题,必现概率低
- 崩溃栈不直观,看不出是哪个对象野掉
五、产生野 / 悬垂指针 常见场景(iOS 实际开发)
- MRC 未手动置 nil
objc
[_person release];
// 没写 _person = nil; 变成悬垂指针
- Block 循环引用 + 对象提前释放
- 代理不写 weak,用了 strong控制器销毁后,代理指针变成悬垂,回调直接崩
- C 语言 malloc/free 不置空
c
运行
free(p);
// 不 p = NULL; 悬垂指针
- 栈变量地址外传、数组越界、内存踩踏
六、如何规避野指针 & 悬垂指针(工程必备)
1. OC 开发
- 一律用 ARC,减少手动内存管理失误
- 代理、闭包回调全部用 weak
- 对象释放 / 不用时主动赋值 nil
- 容器存对象避免悬空引用
2. C 内存开发
malloc/calloc/realloc用完必须freefree后立刻手动置空:p = NULL- 绝不返回栈变量地址
3. 调试手段
- Xcode 开启 Zombies 排查已销毁对象访问
- 开启地址消毒、内存越界检测
- 静态分析 Analyze 提前查出野指针风险
七、总结
- 空指针:地址为 0,不指向有效内存,OC 给 nil 发消息安全不崩溃。
- 野指针:指针指向随机非法内存地址,访问直接 EXC_BAD_ACCESS 崩溃,多因未初始化、越界、乱指。
- 悬垂指针:原本指向合法内存,内存释放 / 销毁后指针未置空,依旧持有旧地址,悬空无依托,访问同样会野指针崩溃。
- 僵尸指针:对象已销毁,内存被标记为僵尸不回收,用于调试定位野指针调用栈。核心区别:野是乱指,悬垂是释放后还吊着。