编程进阶网 编程进阶网
首页
  • 在线工具
  • JSON工具
  • 文本工具
  • 图片处理
  • 文档转化
  • 代码压缩
  • 加解密
  • 时间日期
  • 网络工具
  • 颜色设计
  • 二维码
  • 开发实用
  • 计算机组成原理
  • 操作系统原理
  • 网络协议原理
  • 数据库系统原理
  • 序卷导读
  • 数据本质
  • 运行模型
  • 并发设计
  • 内存真相
  • 交互系统
  • 面向对象
  • 设计原则
  • 设计模式
  • 系统架构
  • 体系建设
  • 代码品质
  • 方案设计
  • 稳定可靠
  • 工程运维
  • 性能优化
  • 数据结构导论
  • 线性结构详解
  • 树哈希结构论
  • 容器设计实战
  • 经典算法思想
  • 工程案例剖析
  • 算法题库精练
  • C语言入门
  • C综合案例
  • C专栏博客
  • C标准集库
  • C++入门教程
  • C++综合案例
  • C++专栏博客
  • C++编程技巧
  • Java入门教程
  • Java综合案例
  • Java专栏博客
  • Go入门教程
  • Go综合案例
  • Go专栏博客
  • Go开发技巧
  • JavaScript入门
  • JavaScript案例
  • JavaScript高级
  • Android库解读
  • Android专栏
  • iOS ObjC入门
  • iOS Swift入门
  • iOS入门精通
  • Web之Html手册
  • Web之TypeScript
  • Web之Vue高级进阶
  • Linux之QML入门
  • Linux之QT核心库
  • Python教程
  • Shell&Bash教程
  • 工具脚本
  • 自动化脚本
  • 质量保障
  • 产品思考
  • 软实力
  • 开发流程
  • Git应用
  • 技术模版
  • 技术规范
  • Markdown
  • Mermaid
  • 开源协议
  • 毛选解读
  • 自我精进
  • 关于我
  • 自我精进
  • 职场管理
  • 职场面试
  • 心情杂货
  • 友情链接

杨充

专注编程 · 终身学习者
首页
  • 在线工具
  • JSON工具
  • 文本工具
  • 图片处理
  • 文档转化
  • 代码压缩
  • 加解密
  • 时间日期
  • 网络工具
  • 颜色设计
  • 二维码
  • 开发实用
  • 计算机组成原理
  • 操作系统原理
  • 网络协议原理
  • 数据库系统原理
  • 序卷导读
  • 数据本质
  • 运行模型
  • 并发设计
  • 内存真相
  • 交互系统
  • 面向对象
  • 设计原则
  • 设计模式
  • 系统架构
  • 体系建设
  • 代码品质
  • 方案设计
  • 稳定可靠
  • 工程运维
  • 性能优化
  • 数据结构导论
  • 线性结构详解
  • 树哈希结构论
  • 容器设计实战
  • 经典算法思想
  • 工程案例剖析
  • 算法题库精练
  • C语言入门
  • C综合案例
  • C专栏博客
  • C标准集库
  • C++入门教程
  • C++综合案例
  • C++专栏博客
  • C++编程技巧
  • Java入门教程
  • Java综合案例
  • Java专栏博客
  • Go入门教程
  • Go综合案例
  • Go专栏博客
  • Go开发技巧
  • JavaScript入门
  • JavaScript案例
  • JavaScript高级
  • Android库解读
  • Android专栏
  • iOS ObjC入门
  • iOS Swift入门
  • iOS入门精通
  • Web之Html手册
  • Web之TypeScript
  • Web之Vue高级进阶
  • Linux之QML入门
  • Linux之QT核心库
  • Python教程
  • Shell&Bash教程
  • 工具脚本
  • 自动化脚本
  • 质量保障
  • 产品思考
  • 软实力
  • 开发流程
  • Git应用
  • 技术模版
  • 技术规范
  • Markdown
  • Mermaid
  • 开源协议
  • 毛选解读
  • 自我精进
  • 关于我
  • 自我精进
  • 职场管理
  • 职场面试
  • 心情杂货
  • 友情链接
  • README
  • Android提升进阶

  • iOS开发和进阶

    • README
    • ObjC编程入门

      • README
      • ObjC基础语法
      • 面向对象编程
      • ObjC高级语法
      • ObjC内存管理
        • 4.1 生命周期设计
          • 4.1.1 初始化操作
          • 4.1.2 销毁时释放
        • 4.2 内存管理
          • 4.2.1 引用计数
          • 4.2.2 引用计数规则
          • 4.2.3 内存管理方式
        • 4.3 手动引用计数
          • 4.3.1 MRC案例
          • 4.3.2 MRC案例分析
          • 4.3.3 原理设计
          • 4.3.4 使用自动释放池
        • 4.4 自动引用计数
          • 4.4.1 ARC案例
          • 4.4.2 ARC案例分析
          • 4.4.3 原理设计
        • 4.5 内存最佳实践
          • 4.5.1 避免循环引用
          • 4.5.2 管理临时对象
          • 4.5.3 dealloc
          • 4.5.4 MRC vs ARC
        • 4.6 循环引用排查与实战
          • 4.6.1 循环引用的三种经典形态
          • 4.6.2 weak-strong dance 详解
          • 4.6.3 使用 Xcode 排查循环引用
        • 4.7 ARC 下的常见坑
        • 4.8 一个实战案例:内存泄漏排查
    • Swift编程入门

    • 专栏博客

  • Web开发和进阶

  • Linux应用开发

  • IoT智能硬件开发

  • Apps
  • iOS开发和进阶
  • ObjC编程入门
杨充
2025-07-23
目录

ObjC内存管理

# 04.ObjC内存管理

# 目录介绍

  • 4.1 生命周期设计
    • 4.1.1 初始化操作
    • 4.1.2 销毁时释放
  • 4.2 内存管理
    • 4.2.1 引用计数
    • 4.2.2 引用计数规则
    • 4.2.3 内存管理方式
  • 4.3 手动引用计数
    • 4.3.1 MRC案例
    • 4.3.2 MRC案例分析
    • 4.3.3 原理设计
    • 4.3.4 使用自动释放池
  • 4.4 自动引用计数
    • 4.4.1 ARC案例
    • 4.4.2 ARC案例分析
    • 4.4.3 原理设计
  • 4.5 内存最佳实践
    • 4.5.1 避免循环引用
    • 4.5.2 管理临时对象
    • 4.5.3 dealloc
    • 4.5.4 MRC vs ARC

# 4.1 生命周期设计

对象的生命周期是指对象从创建到销毁的整个过程。了解对象的生命周期对于正确管理内存和资源以及避免潜在的问题非常重要。

# 4.1.1 初始化操作

1.分配(Allocation):在分配阶段,通过使用alloc和init方法或其他创建对象的方法,为对象分配内存空间,并初始化对象的实例变量。

MyObject *obj = [[MyObject alloc] init];
  • alloc:为对象分配内存。
  • init:初始化对象,设置默认值或执行其他初始化操作。

2.初始化(Initialization):在初始化阶段,可以在init方法中对对象的实例变量进行初始化,设置初始状态和执行其他必要的操作。

- (instancetype)init {
    self = [super init];
    if (self) {
        // 初始化实例变量
    }
    return self;
}

// 任何类在实例化之前都会先调用该方法
+ (void)initialize {  
    //这个比init方法先调用
}

# 4.1.2 销毁时释放

1.释放(Deallocation):在释放阶段,对象不再被使用,内存空间将被释放。当对象不再被引用时,Objective-C的自动引用计数(ARC)机制会自动处理对象的释放。

obj = nil; // 对象将被释放

2.释放前的清理(Cleanup before Deallocation):在对象被释放之前,可以在dealloc方法中执行一些清理操作,如释放资源、取消观察者、解除通知等。

// 对应于 initialize 方法,用于清空对象,当引用计数为 0 时被调用
- (void)dealloc {
    // 如果未启用 ARC,则需要手动释放引用计数
    [nickName release];
    // 清理操作
    [super dealloc];
}

# 4.2 内存管理

# 4.2.1 引用计数

引用计数是 Objective-C 内存管理的核心机制。每个对象都有一个引用计数器,用于记录当前有多少个对象在使用它。

# 4.2.2 引用计数规则

  • 当对象被创建时,引用计数为 1。
  • 当对象被其他对象引用时,引用计数加 1。
  • 当对象不再被引用时,引用计数减 1。
  • 当引用计数为 0 时,对象被销毁,内存被释放。

# 4.2.3 内存管理方式

内存管理是一项重要的任务,用于管理对象的生命周期和内存分配。在Objective-C中,有两种主要的内存管理方式:

手动引用计数(Manual Reference Counting,MRC)和自动引用计数(Automatic Reference Counting,ARC)。

# 4.3 手动引用计数

# 4.3.1 MRC案例

手动引用计数(MRC):在MRC中,开发者需要手动管理对象的引用计数,通过调用retain、release和autorelease等方法来增加或减少对象的引用计数。需要确保在不再使用对象时,适时地释放其内存。

MyObject *obj = [[MyObject alloc] init]; // 引用计数为1
[obj retain]; // 增加引用计数
//释放对象
[obj release]; // 减少引用计数
[obj autorelease]; // 在自动释放池中延迟释放

# 4.3.2 MRC案例分析

手动管理引用计数的方法

  • retain:增加对象的引用计数。
  • release:减少对象的引用计数。
  • autorelease:将对象添加到自动释放池,稍后释放。
  • dealloc:对象被销毁时调用的方法,用于释放资源。
// 创建对象,引用计数为 1
NSObject *obj = [[NSObject alloc] init];

// 增加引用计数
[obj retain]; // 引用计数为 2

// 减少引用计数
[obj release]; // 引用计数为 1

// 将对象添加到自动释放池
[obj autorelease]; // 引用计数为 1,稍后释放

// 手动释放对象
[obj release]; // 引用计数为 0,对象被销毁

# 4.3.3 原理设计

# 4.3.4 使用自动释放池

自动释放池用于延迟对象的释放。当对象被发送 autorelease 消息时,它会被添加到当前的自动释放池中。当自动释放池被释放时,池中的所有对象都会收到 release 消息。

@autoreleasepool {
    // 创建对象并添加到自动释放池
    NSString *str = [[[NSString alloc] initWithFormat:@"Hello, %@", @"World"] autorelease];

    // 使用对象
    NSLog(@"%@", str);

    // 自动释放池结束时,str 会被释放
}

# 4.4 自动引用计数

ARC 是 Objective-C 的编译器特性,它自动管理对象的引用计数,开发者无需手动调用 retain、release 和 autorelease。

# 4.4.1 ARC案例

自动引用计数(ARC):在ARC中,编译器会自动插入适当的引用计数管理代码,开发者无需手动管理对象的引用计数。编译器会根据代码的上下文自动添加retain、release和autorelease等操作。

MyObject *obj = [[MyObject alloc] init]; // 引用计数为1
obj = nil; // 引用计数 = 0,对象被销毁
// 不需要手动调用retain/release/autorelease

# 4.4.2 ARC案例分析

ARC 的规则

  • 编译器会自动在适当的位置插入 retain、release 和 autorelease。
  • 开发者不能手动调用 retain、release 和 autorelease。
  • 对象的生命周期由编译器管理。

# 4.4.3 原理设计

ARC:编译器自动插入 retain/release(默认模式)

属性修饰符:

  • strong:持有对象(默认)
  • weak:弱引用(自动置 nil)
  • copy:复制对象(用于 NSString/NSArray)
  • assign:基本数据类型(非对象)

1.强引用(Strong Reference):默认情况下,对象之间的引用是强引用,即强制保持对对象的引用,只有当所有强引用都被释放时,对象才会被释放。

// 默认值,变量会被保存在内存中直到离开作用域
__strong NSString *strongString;

2.弱引用(Weak Reference):使用__weak修饰符可以创建弱引用,弱引用不会增加对象的引用计数,当对象被释放时,弱引用会自动设置为nil。

// 弱引用,假如引用的对象被释放,那么弱引用会被设为 nil
__weak MyObject *weakObj = obj;

// 与弱引用类似,但是如果引用的对象被释放,引用也不会被设为 nil
__unsafe_unretained NSArray *unsafeArray;

3.循环引用(Retain Cycle):循环引用指的是两个或多个对象之间相互持有强引用,导致它们无法被释放。为了避免循环引用,可以使用__weak修饰符、__unsafe_unretained修饰符或使用弱引用的代理对象等方式。

__weak typeof(self) weakSelf = self;

4.手动释放内存:在MRC中,需要手动释放对象的内存,可以在适当的时机调用release方法。在ARC中,不需要手动释放内存,编译器会自动插入释放代码。

# 4.5 内存最佳实践

# 4.5.1 避免循环引用

循环引用会导致内存泄漏。使用 weak 引用或 __weak 修饰符来打破循环引用。

__weak typeof(self) weakSelf = self;
self.block = ^{
    [weakSelf doSomething]; // 使用 weakSelf 避免循环引用
};

# 4.5.2 管理临时对象

在循环中创建大量临时对象时,使用 @autoreleasepool 及时释放内存。

for (int i = 0; i < 1000; i++) {
    @autoreleasepool {
        NSString *tempStr = [NSString stringWithFormat:@"Temp %d", i];
        NSLog(@"%@", tempStr);
    }
}

# 4.5.3 dealloc

在 dealloc 方法中释放对象持有的资源。

# 示例:

- (void)dealloc {
    // 释放资源
    [_resource release];
    [super dealloc];
}

# 4.5.4 MRC vs ARC

手动内存管理与 ARC 的对比

特性 手动内存管理 ARC
引用计数管理 手动调用 retain、release 编译器自动管理
代码复杂度 较高,容易出错 较低,减少内存管理错误
性能 需要手动优化 编译器优化,性能较好
适用场景 需要精细控制内存的场合 大多数现代 Objective-C 项目

# 4.6 循环引用排查与实战

循环引用(Retain Cycle)是 ObjC 开发中最隐蔽的内存泄漏来源,单靠眼睛看代码很难发现。

# 4.6.1 循环引用的三种经典形态

形态一:对象间互相强引用。 A 持有 B 的 strong 引用,B 又持有 A 的 strong 引用,两者永远无法释放。

@interface Parent : NSObject
@property (nonatomic, strong) Child *child;
@end

@interface Child : NSObject
@property (nonatomic, strong) Parent *parent; // ⚠️ 循环引用
@end

修复:将其中一方改为 weak:

@property (nonatomic, weak) Parent *parent; // ✅ 打破循环

形态二:Block 捕获 self。 对象持有 Block,Block 内部又强引用了该对象。

self.completionBlock = ^{
    [self doSomething]; // ⚠️ self → block → self
};

修复:使用 __weak 打破强引用链:

__weak typeof(self) weakSelf = self;
self.completionBlock = ^{
    [weakSelf doSomething]; // ✅ 弱引用打破循环
};

形态三:NSTimer 持有 target。 Timer 被 RunLoop 持有,Timer 又强引用 target,导致 target 无法释放。这是最容易被忽略的循环引用。

[NSTimer scheduledTimerWithTimeInterval:1.0
    target:self // ⚠️ timer → self,RunLoop → timer
    selector:@selector(tick)
    userInfo:nil
    repeats:YES];

修复:使用 iOS 10+ 的 Block API,配合 weak-strong dance:

__weak typeof(self) weakSelf = self;
[NSTimer scheduledTimerWithTimeInterval:1.0 repeats:YES block:^(NSTimer *timer) {
    [weakSelf tick];
}];

# 4.6.2 weak-strong dance 详解

上面的 __weak 写法有一个小风险:如果在 Block 执行过程中 self 被释放,weakSelf 会变成 nil,导致 Block 内所有操作都静默失效。如果希望在 Block 执行期间保证 self 不被释放,使用 weak-strong dance:

__weak typeof(self) weakSelf = self;
self.completionBlock = ^{
    __strong typeof(weakSelf) strongSelf = weakSelf;
    if (!strongSelf) return; // 已经释放,安全退出
    [strongSelf doSomething]; // 此时 strongSelf 保证存活到 Block 结束
    [strongSelf doAnotherThing];
};

原理:strongSelf 是一把"临时的强引用锁"——在 Block 执行期间临时增加引用计数,Block 结束时自动释放。这保证了操作要么完整执行,要么安全跳过,不会出现"做了一半对象没了"的尴尬状态。

# 4.6.3 使用 Xcode 排查循环引用

Xcode 提供了两个强大工具排查内存泄漏:

Instruments - Leaks:Product → Profile → Leaks,运行 App 反复进出可疑页面,观察是否有内存持续增长不回落。

Memory Graph Debugger:运行 App 后点击 Xcode 底部调试栏的"三叉戟"图标,可查看当前所有存活对象及其引用关系。循环引用会显示为两个对象之间互相粗线连接。

实战经验:排查步骤——①先怀疑所有 Block 内部是否用了 self.xxx;②检查所有 delegate 是否声明为 weak;③检查所有 NSTimer 是否正确 invalidate。90% 的内存泄漏逃不出这三个步骤。

# 4.7 ARC 下的常见坑

即使开启了 ARC,仍有一些容易翻车的场景需要手动处理。

坑一:CF 对象和 NS 对象的桥接。 Core Foundation 的 C 对象不受 ARC 管理,需要手动桥接。

// 创建 CF 对象后需要手动用 CFRelease 释放
CFStringRef cfStr = CFStringCreateWithCString(NULL, "hello", kCFStringEncodingUTF8);
NSString *nsStr = (__bridge_transfer NSString *)cfStr; // 所有权转移给 ARC
// 或使用 __bridge 不转移所有权,但需要手动 CFRelease(cfStr);

坑二:performSelector 的内存警告。 ARC 无法确定 performSelector: 返回值的所有权,编译时会警告。尽量避免使用,改用直接调用或 Block。

坑三:大量临时对象不释放。 循环内创建大量临时对象,即使 ARC 也会在 autorelease pool drain 时才释放。使用 @autoreleasepool 包裹循环体可及时释放:

for (int i = 0; i < 100000; i++) {
    @autoreleasepool {
        NSString *str = [NSString stringWithFormat:@"%d", i];
        // ... 使用 str
    } // 每次循环结束时释放 str
}

# 4.8 一个实战案例:内存泄漏排查

假设你发现 App 在反复进出"个人中心"页面后内存持续增长。排查步骤如下:

  1. 用 Memory Graph 发现存在多个未释放的 UserProfileVC 实例。
  2. 点到其中一个,查看引用链:UserProfileVC ↔ UserViewModel ↔ NetworkManager(发现三者互相持有)。
  3. 定位代码:UserProfileVC 持有 UserViewModel(strong),UserViewModel 的回调 Block 捕获了 self(即 UserProfileVC),NetworkManager 的 completion handler 又强引用了 UserViewModel。
  4. 修复:将 UserViewModel 对 VC 的引用改为 weak;回调 Block 内使用 weak-strong dance;NetworkManager 的 handler 改为在回调后置 nil。
  5. 验证:再次运行 Memory Graph,UserProfileVC 在 pop 后正确释放,内存不再增长。
上次更新: 2026/06/28, 17:55:19
ObjC高级语法
README

← ObjC高级语法 README→

最近更新
01
科学方法实践论法
06-28
02
辩证思维矛盾论法
06-28
03
毛选中的调查观念
06-28
更多文章>
Theme by Vdoing | Copyright © 2019-2026 杨充 | MIT License | 鄂ICP备2024073355号-1 | 鄂ICP备2024073355号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式