1. Objective-C 内存管理根本
1.1 堆与栈内存管理
iOS 系统区分堆栈内存,采用差别的管理策略:
• 栈内存特性:
- 自动分配与释放,依照 LIFO (后进先出) 原则
- 存储局部变量、函数参数和返回地点
- 具有极高的访问速度与服从
- 容量有限,通常在线程创建时固定大小
• 堆内存特性:
- 动态分配与释放,需要手动或自动引用计数管理
- 存储对象实例、全局数据和长命命周期资源
- 访问速度相对较慢,但容量更大
- 可能发生内存碎片,需要定期整理
- // 栈内存分配示例
- void stackAllocationExample(void) {
- // 在栈上分配的基本类型和结构体
- int localInteger = 42; // 栈上的整数变量
- CGRect frame = CGRectMake(0, 0, 100, 100); // 栈上的结构体
-
- // 离开函数作用域后,栈变量自动释放
- }
- // 堆内存分配示例
- NSObject* heapAllocationExample(void) {
- // 在堆上分配对象
- NSObject *object = [[NSObject alloc] init]; // 在堆上分配,需要手动管理
-
- // 在ARC下会自动管理引用计数
- return object; // 对象的所有权转移给调用者
- }
复制代码 1.2 基于地区的分配策略
iOS 使用基于地区的内存分配策略,优化特定场景下的内存使用服从:
• 地区界说:将内存划分为具有相似生命周期的逻辑地区
• 自动池地区:用于短生命周期对象的临时分配
• VM 地区:内核层面的内存映射地区管理
• 地区销毁优化:整体释放地区内全部对象,制止逐个释放的开销
- // 自动释放池使用示例
- - (void)processingLargeDataSet {
- @autoreleasepool {
- // 在此作用域内创建的临时对象将在自动释放池销毁时一起释放
- for (int i = 0; i < 100000; i++) {
- NSString *tempString = [NSString stringWithFormat:@"Item %d", i];
- // 处理字符串...
- // tempString 将在当前自动释放池销毁时被释放
- }
- } // 自动释放池在此处销毁,释放池内所有对象
-
- // 继续其他处理...
- }
复制代码 1.3 内存映射原理
iOS 内存映射体系提供了高效的文件访问与数据共享机制:
• 文件映射:将文件内容直接映射到虚拟内存地点空间
• 共享地区:多进程间共享内存地区,实现高效通讯
• 写时复制策略:仅在修改时创建私有内存页面副本
• 延迟加载:按需将数据从存储装备读入物理内存
- // 使用内存映射访问大文件的示例
- - (NSData *)efficientReadLargeFile:(NSString *)path {
- NSError *error;
- NSData *mappedData = [NSData dataWithContentsOfFile:path
- options:NSDataReadingMappedIfSafe
- error:&error];
- if (error) {
- NSLog(@"Memory mapping failed: %@", error);
- return nil;
- }
-
- // 现在可以高效访问文件数据,无需一次性读入内存
- return mappedData;
- }
复制代码 1.4 运行时内存模型布局
Objective-C 运行时系统构建了一个复杂的内存模型,支持动态特性:
• 类布局内存布局:存储类界说、方法表与实例变量布局
• 对象内存布局:包含 isa 指针和实例变量数据的内存布局
• 方法缓存:加速消息分发的方法查找缓存机制
• 元数据表:用于动态范例系统的内存布局
- // 运行时检查对象内存结构示例
- - (void)examineObjectMemoryLayout {
- // 创建测试对象
- NSObject *testObject = [[NSObject alloc] init];
-
- // 使用运行时函数获取类信息
- Class objectClass = object_getClass(testObject);
- Class metaClass = object_getClass(objectClass);
-
- // 获取实例大小
- size_t instanceSize = class_getInstanceSize(objectClass);
-
- // 获取类中的方法数量
- unsigned int methodCount = 0;
- Method *methods = class_copyMethodList(objectClass, &methodCount);
-
- NSLog(@"对象地址: %p", testObject);
- NSLog(@"类对象地址: %p", objectClass);
- NSLog(@"元类地址: %p", metaClass);
- NSLog(@"实例大小: %zu 字节", instanceSize);
- NSLog(@"方法数量: %u", methodCount);
-
- // 释放方法列表
- free(methods);
- }
复制代码 2. 引用计数与全部权语义
Objective-C 的内存管理核心基于引用计数和全部权语义,确保对象生命周期的可预测性和安全性。
2.1 引用计数基本原理
• 对象创建时引用计数设为 1
• 每增加一个拥有者,引用计数加 1
• 每失去一个拥有者,引用计数减 1
• 引用计数降为 0 时,对象被销毁
2.2 全部权语义规则
• 强引用:拥有对象,防止其被释放
• 弱引用:不拥有对象,对象销毁时自动置为 nil
• 自动释放:延迟释放全部权直到当前自动释放池销毁
• 拷贝:创建独立的对象副本,确保完全全部权
- // 手动引用计数示例 (非 ARC)
- - (void)manualReferenceCountingExample {
- // 创建对象,初始引用计数为 1
- NSObject *object = [[NSObject alloc] init];
-
- // 增加引用计数 (现在是 2)
- [object retain];
-
- // 减少引用计数 (现在是 1)
- [object release];
-
- // 将对象加入自动释放池,延迟释放
- [object autorelease];
-
- // 自动释放池销毁时,对象引用计数减 1,若降为 0 则销毁对象
- }
- // ARC 下的所有权修饰符示例
- @interface OwnershipExample : NSObject
- @property (strong, nonatomic) NSObject *strongReference; // 强引用,拥有对象
- @property (weak, nonatomic) NSObject *weakReference; // 弱引用,不拥有对象
- @property (copy, nonatomic) NSString *copiedString; // 拷贝,拥有独立副本
- @property (unsafe_unretained, nonatomic) NSObject *unsafeReference; // 不安全引用
- @end
复制代码 3. 引用计数核心实现
Objective-C 引用计数实现机制采用复杂的内部数据布局和优化技术,确保高效实行。
3.1 SideTables 架构与内部机制
iOS 运行时系统使用 SideTables 架构存储和管理引用计数:
• SideTables 数组:按对象地点哈希分散引用计数,淘汰锁争用
• SideTable 布局:每个表包含锁、引用计数表和弱引用表
• 自旋锁保护:使用轻量级锁机制保护表操作
• 分布式引用计数:制止单点瓶颈,进步并发性能
- // SideTables 结构伪代码表示
- struct SideTable {
- spinlock_t lock; // 自旋锁,保护表访问
- RefcountMap refcounts; // 引用计数哈希表
- weak_table_t weak_table; // 弱引用表
- };
- // 全局 SideTables 数组
- static SideTable SideTables[64]; // 通常是 CPU 核心数量的倍数
- // 查找对象对应的 SideTable
- static SideTable& getSideTableForObject(id object) {
- uintptr_t address = (uintptr_t)object;
- int index = (address >> 4) & 0x3f; // 取地址右移 4 位后低 6 位作为索引
- return SideTables[index];
- }
复制代码 3.2 哈希表存储引用计数
引用计数存储在高效的哈希表布局中:
• RefcountMap 实现:使用自界说哈希表存储对象地点到引用计数的映射
• 地点位移技术:使用对象地点中的空闲位存储额外信息
• 引用计数压缩:小引用计数值直接编码在表项中
• 溢出处置惩罚:大引用计数值使用额外内存存储
- // 引用计数操作伪代码
- void retain(id object) {
- SideTable& table = getSideTableForObject(object);
-
- // 加锁保护对表的访问
- table.lock.lock();
-
- // 查找或创建引用计数记录
- RefcountMap::iterator it = table.refcounts.find(object);
- if (it == table.refcounts.end()) {
- // 新对象,插入初始引用计数 1
- table.refcounts[object] = 1;
- } else {
- // 增加引用计数
- it->second += 1;
- }
-
- // 释放锁
- table.lock.unlock();
- }
- bool release(id object) {
- SideTable& table = getSideTableForObject(object);
- bool shouldDealloc = false;
-
- // 加锁保护对表的访问
- table.lock.lock();
-
- // 查找引用计数记录
- RefcountMap::iterator it = table.refcounts.find(object);
- if (it != table.refcounts.end()) {
- // 减少引用计数
- if (it->second > 1) {
- it->second -= 1;
- } else {
- // 引用计数降为0,标记待释放
- table.refcounts.erase(it);
- shouldDealloc = true;
- }
- }
-
- // 释放锁
- table.lock.unlock();
-
- return shouldDealloc;
- }
复制代码 3.3 TaggedPointer 对小型对象的优化
TaggedPointer 是 iOS 内存优化技术,针对小整数和短字符串对象:
• 直接值存储:将数据直接编码在指针中,制止堆分配
• 标记位技术:使用指针低位特别标记辨认 TaggedPointer
• 内存节省:淘汰小对象的内存占用和引用计数开销
• 性能提升:消除引用计数操作和内存释放,进步访问速度
- // TaggedPointer 检测示例
- - (void)demonstrateTaggedPointer {
- // 创建小整数 NSNumber
- NSNumber *smallNumber = @42;
- NSNumber *largeNumber = @(0x1FFFFFFFFFFFFFFF); // 大数值
-
- // 创建短字符串和长字符串
- NSString *shortString = @"abc";
- NSString *longString = @"这是一个很长的字符串,不会使用TaggedPointer优化";
-
- // 打印地址检查是否为 TaggedPointer
- NSLog(@"smallNumber: %p", smallNumber); // 可能显示类似: 0xb000000000000022a
- NSLog(@"largeNumber: %p", largeNumber); // 常规对象地址,如: 0x600001c44350
- NSLog(@"shortString: %p", shortString); // 可能显示为 TaggedPointer
- NSLog(@"longString: %p", longString); // 常规对象地址
-
- // 演示TaggedPointer特性
- CFStringRef cfShort = (__bridge CFStringRef)shortString;
- Boolean isTagged = _CFIsObjC(CFStringGetTypeID(), cfShort);
- NSLog(@"shortString是TaggedPointer: %@", isTagged ? @"是" : @"否");
- }
复制代码 3.4 对象生命周期控制模型
Objective-C 对象生命周期依照明确的控制模型:
• 对象创建阶段:分配内存并初始化实例变量
• 强引用控制:通过引用计数跟踪对象全部权
• 生命周期检测:内存告诫时辨认可释放对象
• 弱引用清零:对象释放时自动将其弱引用置零
• 销毁流程:dealloc 方法回收关联资源
- // ARC 下对象生命周期示例
- @implementation LifecycleExample
- - (instancetype)init {
- self = [super init];
- if (self) {
- NSLog(@"init: 对象创建,引用计数为 1");
- // 初始化代码...
- }
- return self;
- }
- - (void)dealloc {
- NSLog(@"dealloc: 对象销毁,引用计数降为 0");
- // 清理代码...
- // 注意:在 ARC 下不调用 [super dealloc]
- }
- @end
- // 使用上述对象
- - (void)demonstrateLifecycle {
- @autoreleasepool {
- LifecycleExample *example = [[LifecycleExample alloc] init];
- NSLog(@"对象已创建");
-
- // 强引用控制示例
- {
- LifecycleExample *ref = example; // 引用计数加1
- NSLog(@"作用域内增加引用");
- } // 作用域结束,引用计数减1
-
- NSLog(@"作用域外,对象仍存在");
-
- // 弱引用示例
- __weak LifecycleExample *weakRef = example;
-
- example = nil; // 原始引用清零,如果引用计数降至0,对象被销毁
-
- NSLog(@"弱引用状态: %@", weakRef ? @"仍指向对象" : @"已自动置零");
- }
- }
复制代码 4. 全部权语义框架
Objective-C 全部权语义框架界说了对象管理的核心规则和设计模式。
4.1 全部权转移协议
全部权转移协议规定对象全部权在差别上下文间的传递规则:
• 基本原则:
- 全部权可以转移但不可凭空创建或消散
- 创建对象的方法拥有全部权并必须转移或释放
- 获取全部权的一方负责最终释放
• 命名约定:
- alloc/new/copy/mutableCopy 为创建方法,调用者获得全部权
- get 前缀方法不转移全部权
- 非全部权方法返回的对象被加入自动释放池
- // 所有权转移示例
- - (NSArray *)createAndTransferOwnership {
- // 创建对象,获得所有权
- NSMutableArray *array = [[NSMutableArray alloc] init];
- [array addObject:@"Item 1"];
- [array addObject:@"Item 2"];
-
- // 返回不可变副本,转移所有权给调用者
- return [array copy]; // 创建新对象并转移所有权
- }
- // 使用并接收所有权
- - (void)receiveAndManageOwnership {
- NSArray *receivedArray = [self createAndTransferOwnership];
- // 现在我们拥有 receivedArray 的所有权
-
- // 在 ARC 下,离开作用域时自动释放
- }
复制代码 4.2 对象图关系管理
Objective-C 内存管理需处置惩罚复杂的对象图关系:
• 循环引用题目:
- 强引用循环导致内存泄漏
- 使用弱引用打破循环
- 父子对象通常使用强-弱引用模式
• 全部权图设计:
- 明确全部权树状布局
- 制止引用循环,设计单向全部权
- 使用委托和观察者模式时制止强引用
- // 循环引用示例与解决方案
- @interface Parent : NSObject
- @property (strong, nonatomic) Child *child; // 强引用子对象
- @end
- @interface Child : NSObject
- @property (weak, nonatomic) Parent *parent; // 弱引用父对象,避免循环
- @end
- // 闭包捕获中的循环引用示例
- - (void)setupCircularReference {
- // 创建一个对象
- self.dataHandler = [[DataHandler alloc] init];
-
- // 错误方式:创建循环引用
- self.dataHandler.completionBlock = ^{
- [self processCompletedData]; // 闭包强引用 self
- };
-
- // 正确方式:使用弱引用打破循环
- __weak typeof(self) weakSelf = self;
- self.dataHandler.completionBlock = ^{
- __strong typeof(weakSelf) strongSelf = weakSelf;
- if (strongSelf) {
- [strongSelf processCompletedData]; // 安全地使用 self
- }
- };
- }
复制代码 4.3 内存分配与释放模式
Objective-C 提供多种内存分配模式,适应差别场景需求:
• 通例分配模式:
- 按需分配独立对象,独立管理生命周期
- 使用 alloc/init 显式分配
- 推迟释放使用 autorelease
• 批量对象处置惩罚:
- 自动释放池优化多个临时对象释放
- 缓存池重用常用对象淘汰分配开销
- 懒加载延迟初始化直到初次访问
- // 自动释放池优化
- - (void)processImagesWithAutoreleasePools {
- NSArray *imageURLs = @[ /* 大量URL... */ ];
-
- for (NSString *imageURL in imageURLs) {
- @autoreleasepool {
- // 在循环内部创建自动释放池
- UIImage *image = [UIImage imageWithContentsOfFile:imageURL];
- [self processImage:image];
-
- // 每次循环结束释放临时对象,避免内存峰值
- }
- }
- }
- // 对象缓存池示例
- @implementation ImageCache {
- NSCache *_imageCache;
- }
- - (instancetype)init {
- self = [super init];
- if (self) {
- _imageCache = [[NSCache alloc] init];
- _imageCache.countLimit = 50; // 限制缓存对象数量
- _imageCache.totalCostLimit = 10 * 1024 * 1024; // 限制内存使用量
- }
- return self;
- }
- - (UIImage *)imageForKey:(NSString *)key {
- UIImage *cachedImage = [_imageCache objectForKey:key];
- if (!cachedImage) {
- // 缓存未命中,创建新对象
- cachedImage = [self loadImageForKey:key];
- if (cachedImage) {
- // 将对象添加到缓存
- [_imageCache setObject:cachedImage forKey:key cost:[self costForImage:cachedImage]];
- }
- }
- return cachedImage;
- }
复制代码 4.4 跨线程内存管理留意事项
多线程环境下的内存管理需要额外留意:
• 线程安全考虑:
- 制止多线程同时修改共享对象
- 使用线程安全集合或添加同步机制
- 主线程专属对象(如 UI 组件)不应在背景线程访问
• 跨线程对象传递:
- 使用 copy 传递不可变对象制止竞态条件
- 引用计数操作的原子性与非原子性选择
- 延迟释放确保对象在其他线程完成使用
- // 线程安全的单例模式
- + (instancetype)sharedInstance {
- static id sharedInstance = nil;
- static dispatch_once_t onceToken;
-
- // 确保线程安全且只初始化一次
- dispatch_once(&onceToken, ^{
- sharedInstance = [[self alloc] init];
- });
-
- return sharedInstance;
- }
- // 多线程环境下的对象传递
- - (void)processDataInBackground {
- // 捕获当前线程的对象
- NSArray *dataItems = [self.dataSource copy]; // 创建不可变副本
-
- dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
- // 在后台线程处理数据
- for (DataItem *item in dataItems) {
- [self processItem:item];
- }
-
- // 结果回到主线程更新 UI
- dispatch_async(dispatch_get_main_queue(), ^{
- [self updateUIWithProcessedData];
- });
- });
- }
复制代码 5. 自动引用计数 (ARC) 系统
自动引用计数是 Apple 在 iOS 5 中引入的内存管理机制,它通过自动插入内存管理代码,解放开辟者从手动管理内存的负担中。在 ARC 之前,开辟者需要依照严酷的"拥有权"规则,手动调用 retain 和 release 方法,这不但增加了开辟难度,也是潜伏错误的重要泉源。
ARC 的核心机制是通过编译器自动在适当位置插入内存管理代码,使对象的生命周期能够得到精确控制。与垃圾回收差别,ARC 在编译时确定对象的生命周期,因此没有运行时的性能开销。
5.1 编译器辅助内存管理
ARC 实现原理
ARC 的核心实现依赖于编译器的静态分析本领。LLVM 编译器会分析代码中的对象生命周期,自动在适当位置插入 retain、release 和 autorelease 调用。这一过程重要发生在编译阶段,详细步骤如下:
- 引用计数增加场景:对象创建、赋值操作、方法参数传递
- 引用计数淘汰场景:变量超出作用域、对象被重新赋值、显式设置为 nil
- // ARC 下的代码
- UIImage *image = [UIImage imageNamed:@"example"];
- self.imageView.image = image;
- image = nil;
- // 编译器转换后的实际代码(伪代码)
- UIImage *image = [[UIImage imageNamed:@"example"] retain];
- [self.imageView setImage:image];
- [image release];
- image = nil;
复制代码 编译时优化与安全措施
LLVM 编译器不但仅是简单地插入内存管理代码,还会实行多种优化:
- 冗余引用计数操作消除:移除不必要的 retain/release 对
- 快速路径优化:对常见模式举行特别处置惩罚
- 静态分析检查:辨认潜伏的内存管理题目
运行时支持根本办法
虽然 ARC 的重要工作在编译时完成,但它仍然依赖 Objective-C 运行时提供的引用计数根本办法:
- 引用计数存储:对象的引用计数存储在对象的内部或外部表中
- SideTable 数据布局:在多线程环境下提供原子操作支持
- 自动释放池:管理临时对象的生命周期
关键的运行时函数包括:
- // 核心内存管理函数
- id objc_retain(id obj);
- void objc_release(id obj);
- id objc_autorelease(id obj);
复制代码 5.2 ARC 修饰符与属性特性
明确 __strong、__weak、__unsafe_unretained、__autoreleasing
ARC 引入了一系列全部权修饰符,用于控制对象引用的举动:
- __strong:默认修饰符,创建强引用,保持对象存活
- __weak:创建弱引用,不增加引用计数,对象释放时自动置为 nil
- __unsafe_unretained:创建不安全的非拥有引用,不增加引用计数,对象释放后变成悬挂指针
- __autoreleasing:用于标记传递给方法的 out 参数,自动添加到当前自动释放池
- // 强引用(默认)
- __strong UIViewController *strongVC = [[UIViewController alloc] init];
- // 弱引用,避免循环引用
- __weak UIViewController *weakVC = strongVC;
- // 不安全的非拥有引用
- __unsafe_unretained UIViewController *unsafeVC = strongVC;
- // 自动释放引用(通常用于指针的指针)
- NSError *__autoreleasing *error = &errorPtr;
复制代码 属性举动:strong、weak、copy、assign
在属性声明中,我们可以使用差别的内存管理属性:
- strong:默认属性,创建强引用,适用于大多数对象
- weak:创建弱引用,适用于委托、父-子关系等场景
- copy:在赋值时创建对象的副本,适用于不可变对象(如 NSString)
- assign:简单赋值,不涉及引用计数,适用于基本数据范例和 unsafe_unretained 对象
- @interface ProfileViewController : UIViewController
- // 强引用属性(默认)
- @property (strong, nonatomic) UIImageView *avatarImageView;
- // 弱引用属性,避免循环引用
- @property (weak, nonatomic) id<ProfileViewControllerDelegate> delegate;
- // 复制属性,确保属性的不可变性
- @property (copy, nonatomic) NSString *username;
- // 简单赋值,用于基本数据类型
- @property (assign, nonatomic) NSInteger userAge;
- @end
复制代码 5.3 弱引用表实现与归零机制
弱引用(__weak)是 ARC 一个重要特性,能够制止循环引用题目。实在现依赖于全局弱引用表:
- SideTable 布局:管理弱引用的散列表
- 弱引用注册:当创建弱引用时,将其注册到全局表中
- 引用归零:当对象释放时,运行时系统扫描弱引用表,将全部指向该对象的弱引用置为 nil
5.3 防止引用循环
引用循环是 ARC 下最常见的内存泄漏原因。以下是防止引用循环的策略:
- 使用弱引用:在父-子关系中,子对象应持有父对象的弱引用
- 合理使用 block:在 block 中捕捉 self 时使用弱引用
- 委托模式:委托对象应该使用弱引用持有其委托
- NSTimer 的特别处置惩罚:使用中心对象或 block-based API
- // 在 block 中避免引用循环
- __weak typeof(self) weakSelf = self;
- self.completionHandler = ^{
- __strong typeof(weakSelf) strongSelf = weakSelf;
- if (strongSelf) {
- [strongSelf processData];
- }
- };
- // 避免 NSTimer 引用循环
- // 不推荐的做法
- [NSTimer scheduledTimerWithTimeInterval:1.0
- target:self
- selector:@selector(timerFired:)
- userInfo:nil
- repeats:YES];
- // 推荐的做法 (iOS 10+)
- self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0
- repeats:YES
- block:^(NSTimer * _Nonnull timer) {
- __strong typeof(weakSelf) strongSelf = weakSelf;
- [strongSelf timerFired:timer];
- }];
复制代码 6. 高级内存管理与优化
6.1 高性能内存模式
自动释放池设计与优化
自动释放池(Autorelease Pool)是 Objective-C 内存管理的重要部分,即使在 ARC 下依然广泛使用:
- 基本原理:延迟对象的释放,直到当前池被销毁
- 嵌套布局:自动释放池可以嵌套,形成栈式布局
- 线程相关性:每个线程都有自己的自动释放池栈
- // 自定义自动释放池,减少内存峰值
- @autoreleasepool {
- for (int i = 0; i < 100000; i++) {
- NSString *string = [NSString stringWithFormat:@"String %d", i];
- // 使用 string
- }
- } // 池结束,临时对象立即释放
复制代码 临时对象与快速释放路径
为了进步性能,ARC 引入了快速释放路径(Fast Release Path)等优化:
- 直接释放:当对象的引用计数降至零时直接释放,而非加入自动释放池
- 局部变量优化:编译器尝试在函数作用域竣事时释放局部变量,而非使用自动释放
- return 值优化:对于返回自动释放对象的方法,可以使用 os_retainAutoreleasedReturnValue/os_autoreleasedReturnValue 优化
延迟释放与批量回收技术
在高性能 iOS 应用中,内存管理常常需要衡量即时性和服从:
- 批量回收:自动释放池允许批量回收多个对象,进步服从
- 多线程考虑:背景线程中创建大量对象时,使用自动释放池控制内存占用
- 图像处置惩罚优化:处置惩罚大型图像时,及时释放中心缓冲区
- // 后台线程处理大量数据
- dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
- @autoreleasepool {
- // 处理大量数据
- NSArray *largeArray = [self processLargeDataSet];
-
- // 进一步处理
- NSArray *results = [self furtherProcessArray:largeArray];
-
- // 回到主线程更新 UI
- dispatch_async(dispatch_get_main_queue(), ^{
- [self updateUIWithResults:results];
- });
- } // 自动释放池结束,临时对象释放
- });
复制代码 内存告诫响应框架
iOS 系统会在内存压力大时发送内存告诫,应用应当及时响应:
- UIApplicationDidReceiveMemoryWarningNotification:注册通知监听内存告诫
- didReceiveMemoryWarning:UIViewController 方法,用于响应内存告诫
- 缓存清算:实现缓存系统,能够在内存告诫时清算非必要资源
- // 在视图控制器中响应内存警告
- - (void)didReceiveMemoryWarning {
- [super didReceiveMemoryWarning];
-
- // 清理可重建的资源
- [self.imageCache removeAllObjects];
-
- // 释放离屏渲染的资源
- [self.offscreenRenderingBuffer releaseResources];
-
- // 移除不可见视图控制器
- if (![self isViewLoaded] || !self.view.window) {
- [self.view removeFromSuperview];
- self.view = nil;
- }
- }
复制代码 6.2 诊断与优化技术
内存泄漏检测方法
内存泄漏是 iOS 应用中常见的题目,有多种方法可以检测:
- Instruments 的 Leaks 工具:辨认未被释放的对象
- Xcode 内存调试器:可视化显示对象间的引用关系
- 堆分析:跟踪堆上分配的内存,分析增长趋势
循环引用分析工具
循环引用(retain cycle)是内存泄漏的重要原因之一:
- Xcode 内存图:可视化查看对象之间的引用关系
- FBRetainCycleDetector:Facebook 开源的运行时检测工具
- 静态分析器:在编译时检测潜伏的循环引用题目
- // 使用 Facebook 的 FBRetainCycleDetector 工具检测循环引用
- #import <FBRetainCycleDetector/FBRetainCycleDetector.h>
- FBRetainCycleDetector *detector = [FBRetainCycleDetector new];
- [detector addCandidate:self];
- NSSet *retainCycles = [detector findRetainCycles];
- if ([retainCycles count] > 0) {
- NSLog(@"Found retain cycles: %@", retainCycles);
- }
复制代码 大型内存分配观察
当应用举行大量内存分配时,需要特别关注:
- 分配的泉源:使用 Instruments 的 Allocations 工具跟踪
- 适当缓存策略:制止重复分配大块内存
- 大图像处置惩罚技术:按需降采样、分片处置惩罚
- // 按需处理大型图像
- - (UIImage *)downsampledImageFromURL:(NSURL *)imageURL toSize:(CGSize)targetSize {
- NSDictionary *options = @{
- NSURLCacheStorageAllowed: @NO
- };
-
- NSData *imageData = [NSData dataWithContentsOfURL:imageURL options:0 error:nil];
-
- // 创建 CGImageSource
- CGImageSourceRef imageSource = CGImageSourceCreateWithData((__bridge CFDataRef)imageData, NULL);
-
- // 设置降采样选项
- NSDictionary *downsampleOptions = @{
- (NSString *)kCGImageSourceCreateThumbnailFromImageAlways: @YES,
- (NSString *)kCGImageSourceThumbnailMaxPixelSize: @(MAX(targetSize.width, targetSize.height) * [UIScreen mainScreen].scale),
- (NSString *)kCGImageSourceCreateThumbnailWithTransform: @YES,
- (NSString *)kCGImageSourceShouldCacheImmediately: @YES
- };
-
- // 创建降采样图像
- CGImageRef downsampledImage = CGImageSourceCreateThumbnailAtIndex(imageSource, 0, (__bridge CFDictionaryRef)downsampleOptions);
- UIImage *resultImage = [UIImage imageWithCGImage:downsampledImage];
-
- // 释放资源
- CGImageRelease(downsampledImage);
- CFRelease(imageSource);
-
- return resultImage;
- }
复制代码 低内存处置惩罚策略
在低内存条件下,应用应当优雅地释放资源:
- 优先级系统:对缓存和资源建立优先级系统
- 分级响应:根据内存压力水平采取差别的措施
- 预防机制:制止达到系统逼迫停止的临界点
- // 实现分级内存清理系统
- - (void)setupMemoryHandling {
- // 注册内存警告通知
- [[NSNotificationCenter defaultCenter] addObserver:self
- selector:@selector(handleMemoryWarning:)
- name:UIApplicationDidReceiveMemoryWarningNotification
- object:nil];
- }
- - (void)handleMemoryWarning:(NSNotification *)notification {
- // 确定当前内存压力等级
- UIApplicationState state = [UIApplication sharedApplication].applicationState;
- BOOL isBackground = (state == UIApplicationStateBackground);
-
- // 第一级:清理低优先级缓存
- [self.imageCache clearLowPriorityItems];
-
- // 第二级:如果在后台,清理更多资源
- if (isBackground) {
- [self.imageCache clearAllExceptVisible];
- [self cancelNonEssentialOperations];
- }
-
- // 第三级:极端情况,释放几乎所有可重建的资源
- if (self.systemMemoryPressureExtreme) {
- [self.imageCache clearAll];
- [self purgeViewControllers];
- [self resetToMinimalState];
- }
- }
复制代码 背景应用的内存压力适配
iOS 对背景应用的内存限制更为严酷,需要特别处置惩罚:
- 背景模式优化:进入背景时主动释放资源
- 背景革新策略:使用背景使命 API 时留意内存使用
- 背景使命完成及时通知:制止系统停止使命
- // 应用进入后台时释放资源
- - (void)applicationDidEnterBackground:(UIApplication *)application {
- // 保存必要状态
- [self saveApplicationState];
-
- // 释放大型资源
- [self.mediaCache purgeNonEssentialResources];
-
- // 释放视图资源
- [self cleanupViewHierarchy];
-
- // 通知完成背景任务
- UIBackgroundTaskIdentifier taskID = self.backgroundTaskID;
- if (taskID != UIBackgroundTaskInvalid) {
- [application endBackgroundTask:taskID];
- self.backgroundTaskID = UIBackgroundTaskInvalid;
- }
- }
复制代码 结论
iOS 内存管理已经从最初的手动管理模式发展到如今的 ARC 自动管理机制,极大进步了开辟服从并淘汰了内存相关错误。然而,高性能 iOS 应用仍然需要开辟者深入明确内存管理机制,制止循环引用和内存泄漏,优化内存占用,并合理处置惩罚内存压力。
通过合理使用自动释放池、弱引用、适当的缓存策略以及内存监控工具,你将能够在保持应用性能的同时,提供流畅的用户体验,制止因内存题目导致的瓦解和性能降落。。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |