iOS底层原理系列05-内存管理:从核心原理到高级优化

水军大提督  金牌会员 | 2025-3-16 05:27:41 | 来自手机 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 970|帖子 970|积分 2910

1. Objective-C 内存管理根本

1.1 堆与栈内存管理

iOS 系统区分堆栈内存,采用差别的管理策略:

栈内存特性


  • 自动分配与释放,依照 LIFO (后进先出) 原则
  • 存储局部变量、函数参数和返回地点
  • 具有极高的访问速度与服从
  • 容量有限,通常在线程创建时固定大小
堆内存特性


  • 动态分配与释放,需要手动或自动引用计数管理
  • 存储对象实例、全局数据和长命命周期资源
  • 访问速度相对较慢,但容量更大
  • 可能发生内存碎片,需要定期整理
  1. // 栈内存分配示例
  2. void stackAllocationExample(void) {
  3.     // 在栈上分配的基本类型和结构体
  4.     int localInteger = 42;                       // 栈上的整数变量
  5.     CGRect frame = CGRectMake(0, 0, 100, 100);  // 栈上的结构体
  6.    
  7.     // 离开函数作用域后,栈变量自动释放
  8. }
  9. // 堆内存分配示例
  10. NSObject* heapAllocationExample(void) {
  11.     // 在堆上分配对象
  12.     NSObject *object = [[NSObject alloc] init]; // 在堆上分配,需要手动管理
  13.    
  14.     // 在ARC下会自动管理引用计数
  15.     return object; // 对象的所有权转移给调用者
  16. }
复制代码
1.2 基于地区的分配策略

iOS 使用基于地区的内存分配策略,优化特定场景下的内存使用服从:
地区界说:将内存划分为具有相似生命周期的逻辑地区
自动池地区:用于短生命周期对象的临时分配
VM 地区:内核层面的内存映射地区管理
地区销毁优化:整体释放地区内全部对象,制止逐个释放的开销
  1. // 自动释放池使用示例
  2. - (void)processingLargeDataSet {
  3.     @autoreleasepool {
  4.         // 在此作用域内创建的临时对象将在自动释放池销毁时一起释放
  5.         for (int i = 0; i < 100000; i++) {
  6.             NSString *tempString = [NSString stringWithFormat:@"Item %d", i];
  7.             // 处理字符串...
  8.             // tempString 将在当前自动释放池销毁时被释放
  9.         }
  10.     } // 自动释放池在此处销毁,释放池内所有对象
  11.    
  12.     // 继续其他处理...
  13. }
复制代码
1.3 内存映射原理

iOS 内存映射体系提供了高效的文件访问与数据共享机制:
文件映射:将文件内容直接映射到虚拟内存地点空间
共享地区:多进程间共享内存地区,实现高效通讯
写时复制策略:仅在修改时创建私有内存页面副本
延迟加载:按需将数据从存储装备读入物理内存
  1. // 使用内存映射访问大文件的示例
  2. - (NSData *)efficientReadLargeFile:(NSString *)path {
  3.     NSError *error;
  4.     NSData *mappedData = [NSData dataWithContentsOfFile:path
  5.                                                 options:NSDataReadingMappedIfSafe
  6.                                                   error:&error];
  7.     if (error) {
  8.         NSLog(@"Memory mapping failed: %@", error);
  9.         return nil;
  10.     }
  11.    
  12.     // 现在可以高效访问文件数据,无需一次性读入内存
  13.     return mappedData;
  14. }
复制代码
1.4 运行时内存模型布局

Objective-C 运行时系统构建了一个复杂的内存模型,支持动态特性:
类布局内存布局:存储类界说、方法表与实例变量布局
对象内存布局:包含 isa 指针和实例变量数据的内存布局
方法缓存:加速消息分发的方法查找缓存机制
元数据表:用于动态范例系统的内存布局
  1. // 运行时检查对象内存结构示例
  2. - (void)examineObjectMemoryLayout {
  3.     // 创建测试对象
  4.     NSObject *testObject = [[NSObject alloc] init];
  5.    
  6.     // 使用运行时函数获取类信息
  7.     Class objectClass = object_getClass(testObject);
  8.     Class metaClass = object_getClass(objectClass);
  9.    
  10.     // 获取实例大小
  11.     size_t instanceSize = class_getInstanceSize(objectClass);
  12.    
  13.     // 获取类中的方法数量
  14.     unsigned int methodCount = 0;
  15.     Method *methods = class_copyMethodList(objectClass, &methodCount);
  16.    
  17.     NSLog(@"对象地址: %p", testObject);
  18.     NSLog(@"类对象地址: %p", objectClass);
  19.     NSLog(@"元类地址: %p", metaClass);
  20.     NSLog(@"实例大小: %zu 字节", instanceSize);
  21.     NSLog(@"方法数量: %u", methodCount);
  22.    
  23.     // 释放方法列表
  24.     free(methods);
  25. }
复制代码
2. 引用计数与全部权语义

Objective-C 的内存管理核心基于引用计数和全部权语义,确保对象生命周期的可预测性和安全性。
2.1 引用计数基本原理

• 对象创建时引用计数设为 1
• 每增加一个拥有者,引用计数加 1
• 每失去一个拥有者,引用计数减 1
• 引用计数降为 0 时,对象被销毁
2.2 全部权语义规则

强引用:拥有对象,防止其被释放
弱引用:不拥有对象,对象销毁时自动置为 nil
自动释放:延迟释放全部权直到当前自动释放池销毁
拷贝:创建独立的对象副本,确保完全全部权
  1. // 手动引用计数示例 (非 ARC)
  2. - (void)manualReferenceCountingExample {
  3.     // 创建对象,初始引用计数为 1
  4.     NSObject *object = [[NSObject alloc] init];
  5.    
  6.     // 增加引用计数 (现在是 2)
  7.     [object retain];
  8.    
  9.     // 减少引用计数 (现在是 1)
  10.     [object release];
  11.    
  12.     // 将对象加入自动释放池,延迟释放
  13.     [object autorelease];
  14.    
  15.     // 自动释放池销毁时,对象引用计数减 1,若降为 0 则销毁对象
  16. }
  17. // ARC 下的所有权修饰符示例
  18. @interface OwnershipExample : NSObject
  19. @property (strong, nonatomic) NSObject *strongReference;  // 强引用,拥有对象
  20. @property (weak, nonatomic) NSObject *weakReference;      // 弱引用,不拥有对象
  21. @property (copy, nonatomic) NSString *copiedString;       // 拷贝,拥有独立副本
  22. @property (unsafe_unretained, nonatomic) NSObject *unsafeReference; // 不安全引用
  23. @end
复制代码
3. 引用计数核心实现

Objective-C 引用计数实现机制采用复杂的内部数据布局和优化技术,确保高效实行。
3.1 SideTables 架构与内部机制

iOS 运行时系统使用 SideTables 架构存储和管理引用计数:
SideTables 数组:按对象地点哈希分散引用计数,淘汰锁争用
SideTable 布局:每个表包含锁、引用计数表和弱引用表
自旋锁保护:使用轻量级锁机制保护表操作
分布式引用计数:制止单点瓶颈,进步并发性能
  1. // SideTables 结构伪代码表示
  2. struct SideTable {
  3.     spinlock_t lock;                  // 自旋锁,保护表访问
  4.     RefcountMap refcounts;            // 引用计数哈希表
  5.     weak_table_t weak_table;          // 弱引用表
  6. };
  7. // 全局 SideTables 数组
  8. static SideTable SideTables[64];      // 通常是 CPU 核心数量的倍数
  9. // 查找对象对应的 SideTable
  10. static SideTable& getSideTableForObject(id object) {
  11.     uintptr_t address = (uintptr_t)object;
  12.     int index = (address >> 4) & 0x3f;  // 取地址右移 4 位后低 6 位作为索引
  13.     return SideTables[index];
  14. }
复制代码
3.2 哈希表存储引用计数

引用计数存储在高效的哈希表布局中:
RefcountMap 实现:使用自界说哈希表存储对象地点到引用计数的映射
地点位移技术:使用对象地点中的空闲位存储额外信息
引用计数压缩:小引用计数值直接编码在表项中
溢出处置惩罚:大引用计数值使用额外内存存储

  1. // 引用计数操作伪代码
  2. void retain(id object) {
  3.     SideTable& table = getSideTableForObject(object);
  4.    
  5.     // 加锁保护对表的访问
  6.     table.lock.lock();
  7.    
  8.     // 查找或创建引用计数记录
  9.     RefcountMap::iterator it = table.refcounts.find(object);
  10.     if (it == table.refcounts.end()) {
  11.         // 新对象,插入初始引用计数 1
  12.         table.refcounts[object] = 1;
  13.     } else {
  14.         // 增加引用计数
  15.         it->second += 1;
  16.     }
  17.    
  18.     // 释放锁
  19.     table.lock.unlock();
  20. }
  21. bool release(id object) {
  22.     SideTable& table = getSideTableForObject(object);
  23.     bool shouldDealloc = false;
  24.    
  25.     // 加锁保护对表的访问
  26.     table.lock.lock();
  27.    
  28.     // 查找引用计数记录
  29.     RefcountMap::iterator it = table.refcounts.find(object);
  30.     if (it != table.refcounts.end()) {
  31.         // 减少引用计数
  32.         if (it->second > 1) {
  33.             it->second -= 1;
  34.         } else {
  35.             // 引用计数降为0,标记待释放
  36.             table.refcounts.erase(it);
  37.             shouldDealloc = true;
  38.         }
  39.     }
  40.    
  41.     // 释放锁
  42.     table.lock.unlock();
  43.    
  44.     return shouldDealloc;
  45. }
复制代码
3.3 TaggedPointer 对小型对象的优化

TaggedPointer 是 iOS 内存优化技术,针对小整数和短字符串对象:
直接值存储:将数据直接编码在指针中,制止堆分配
标记位技术:使用指针低位特别标记辨认 TaggedPointer
内存节省:淘汰小对象的内存占用和引用计数开销
性能提升:消除引用计数操作和内存释放,进步访问速度
  1. // TaggedPointer 检测示例
  2. - (void)demonstrateTaggedPointer {
  3.     // 创建小整数 NSNumber
  4.     NSNumber *smallNumber = @42;
  5.     NSNumber *largeNumber = @(0x1FFFFFFFFFFFFFFF);  // 大数值
  6.    
  7.     // 创建短字符串和长字符串
  8.     NSString *shortString = @"abc";
  9.     NSString *longString = @"这是一个很长的字符串,不会使用TaggedPointer优化";
  10.    
  11.     // 打印地址检查是否为 TaggedPointer
  12.     NSLog(@"smallNumber: %p", smallNumber);  // 可能显示类似: 0xb000000000000022a
  13.     NSLog(@"largeNumber: %p", largeNumber);  // 常规对象地址,如: 0x600001c44350
  14.     NSLog(@"shortString: %p", shortString);  // 可能显示为 TaggedPointer
  15.     NSLog(@"longString: %p", longString);    // 常规对象地址
  16.    
  17.     // 演示TaggedPointer特性
  18.     CFStringRef cfShort = (__bridge CFStringRef)shortString;
  19.     Boolean isTagged = _CFIsObjC(CFStringGetTypeID(), cfShort);
  20.     NSLog(@"shortString是TaggedPointer: %@", isTagged ? @"是" : @"否");
  21. }
复制代码
3.4 对象生命周期控制模型

Objective-C 对象生命周期依照明确的控制模型:
对象创建阶段:分配内存并初始化实例变量
强引用控制:通过引用计数跟踪对象全部权
生命周期检测:内存告诫时辨认可释放对象
弱引用清零:对象释放时自动将其弱引用置零
销毁流程:dealloc 方法回收关联资源
  1. // ARC 下对象生命周期示例
  2. @implementation LifecycleExample
  3. - (instancetype)init {
  4.     self = [super init];
  5.     if (self) {
  6.         NSLog(@"init: 对象创建,引用计数为 1");
  7.         // 初始化代码...
  8.     }
  9.     return self;
  10. }
  11. - (void)dealloc {
  12.     NSLog(@"dealloc: 对象销毁,引用计数降为 0");
  13.     // 清理代码...
  14.     // 注意:在 ARC 下不调用 [super dealloc]
  15. }
  16. @end
  17. // 使用上述对象
  18. - (void)demonstrateLifecycle {
  19.     @autoreleasepool {
  20.         LifecycleExample *example = [[LifecycleExample alloc] init];
  21.         NSLog(@"对象已创建");
  22.         
  23.         // 强引用控制示例
  24.         {
  25.             LifecycleExample *ref = example; // 引用计数加1
  26.             NSLog(@"作用域内增加引用");
  27.         } // 作用域结束,引用计数减1
  28.         
  29.         NSLog(@"作用域外,对象仍存在");
  30.         
  31.         // 弱引用示例
  32.         __weak LifecycleExample *weakRef = example;
  33.         
  34.         example = nil; // 原始引用清零,如果引用计数降至0,对象被销毁
  35.         
  36.         NSLog(@"弱引用状态: %@", weakRef ? @"仍指向对象" : @"已自动置零");
  37.     }
  38. }
复制代码
4. 全部权语义框架

Objective-C 全部权语义框架界说了对象管理的核心规则和设计模式。
4.1 全部权转移协议

全部权转移协议规定对象全部权在差别上下文间的传递规则:
基本原则


  • 全部权可以转移但不可凭空创建或消散
  • 创建对象的方法拥有全部权并必须转移或释放
  • 获取全部权的一方负责最终释放
命名约定


  • alloc/new/copy/mutableCopy 为创建方法,调用者获得全部权
  • get 前缀方法不转移全部权
  • 非全部权方法返回的对象被加入自动释放池
  1. // 所有权转移示例
  2. - (NSArray *)createAndTransferOwnership {
  3.     // 创建对象,获得所有权
  4.     NSMutableArray *array = [[NSMutableArray alloc] init];
  5.     [array addObject:@"Item 1"];
  6.     [array addObject:@"Item 2"];
  7.    
  8.     // 返回不可变副本,转移所有权给调用者
  9.     return [array copy]; // 创建新对象并转移所有权
  10. }
  11. // 使用并接收所有权
  12. - (void)receiveAndManageOwnership {
  13.     NSArray *receivedArray = [self createAndTransferOwnership];
  14.     // 现在我们拥有 receivedArray 的所有权
  15.    
  16.     // 在 ARC 下,离开作用域时自动释放
  17. }
复制代码
4.2 对象图关系管理

Objective-C 内存管理需处置惩罚复杂的对象图关系:
循环引用题目


  • 强引用循环导致内存泄漏
  • 使用弱引用打破循环
  • 父子对象通常使用强-弱引用模式
全部权图设计


  • 明确全部权树状布局
  • 制止引用循环,设计单向全部权
  • 使用委托和观察者模式时制止强引用
  1. // 循环引用示例与解决方案
  2. @interface Parent : NSObject
  3. @property (strong, nonatomic) Child *child;  // 强引用子对象
  4. @end
  5. @interface Child : NSObject
  6. @property (weak, nonatomic) Parent *parent;  // 弱引用父对象,避免循环
  7. @end
  8. // 闭包捕获中的循环引用示例
  9. - (void)setupCircularReference {
  10.     // 创建一个对象
  11.     self.dataHandler = [[DataHandler alloc] init];
  12.    
  13.     // 错误方式:创建循环引用
  14.     self.dataHandler.completionBlock = ^{
  15.         [self processCompletedData];  // 闭包强引用 self
  16.     };
  17.    
  18.     // 正确方式:使用弱引用打破循环
  19.     __weak typeof(self) weakSelf = self;
  20.     self.dataHandler.completionBlock = ^{
  21.         __strong typeof(weakSelf) strongSelf = weakSelf;
  22.         if (strongSelf) {
  23.             [strongSelf processCompletedData];  // 安全地使用 self
  24.         }
  25.     };
  26. }
复制代码
4.3 内存分配与释放模式

Objective-C 提供多种内存分配模式,适应差别场景需求:
通例分配模式


  • 按需分配独立对象,独立管理生命周期
  • 使用 alloc/init 显式分配
  • 推迟释放使用 autorelease
批量对象处置惩罚


  • 自动释放池优化多个临时对象释放
  • 缓存池重用常用对象淘汰分配开销
  • 懒加载延迟初始化直到初次访问
  1. // 自动释放池优化
  2. - (void)processImagesWithAutoreleasePools {
  3.     NSArray *imageURLs = @[ /* 大量URL... */ ];
  4.    
  5.     for (NSString *imageURL in imageURLs) {
  6.         @autoreleasepool {
  7.             // 在循环内部创建自动释放池
  8.             UIImage *image = [UIImage imageWithContentsOfFile:imageURL];
  9.             [self processImage:image];
  10.             
  11.             // 每次循环结束释放临时对象,避免内存峰值
  12.         }
  13.     }
  14. }
  15. // 对象缓存池示例
  16. @implementation ImageCache {
  17.     NSCache *_imageCache;
  18. }
  19. - (instancetype)init {
  20.     self = [super init];
  21.     if (self) {
  22.         _imageCache = [[NSCache alloc] init];
  23.         _imageCache.countLimit = 50;  // 限制缓存对象数量
  24.         _imageCache.totalCostLimit = 10 * 1024 * 1024;  // 限制内存使用量
  25.     }
  26.     return self;
  27. }
  28. - (UIImage *)imageForKey:(NSString *)key {
  29.     UIImage *cachedImage = [_imageCache objectForKey:key];
  30.     if (!cachedImage) {
  31.         // 缓存未命中,创建新对象
  32.         cachedImage = [self loadImageForKey:key];
  33.         if (cachedImage) {
  34.             // 将对象添加到缓存
  35.             [_imageCache setObject:cachedImage forKey:key cost:[self costForImage:cachedImage]];
  36.         }
  37.     }
  38.     return cachedImage;
  39. }
复制代码
4.4 跨线程内存管理留意事项

多线程环境下的内存管理需要额外留意:
线程安全考虑


  • 制止多线程同时修改共享对象
  • 使用线程安全集合或添加同步机制
  • 主线程专属对象(如 UI 组件)不应在背景线程访问
跨线程对象传递


  • 使用 copy 传递不可变对象制止竞态条件
  • 引用计数操作的原子性与非原子性选择
  • 延迟释放确保对象在其他线程完成使用
  1. // 线程安全的单例模式
  2. + (instancetype)sharedInstance {
  3.     static id sharedInstance = nil;
  4.     static dispatch_once_t onceToken;
  5.    
  6.     // 确保线程安全且只初始化一次
  7.     dispatch_once(&onceToken, ^{
  8.         sharedInstance = [[self alloc] init];
  9.     });
  10.    
  11.     return sharedInstance;
  12. }
  13. // 多线程环境下的对象传递
  14. - (void)processDataInBackground {
  15.     // 捕获当前线程的对象
  16.     NSArray *dataItems = [self.dataSource copy]; // 创建不可变副本
  17.    
  18.     dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  19.         // 在后台线程处理数据
  20.         for (DataItem *item in dataItems) {
  21.             [self processItem:item];
  22.         }
  23.         
  24.         // 结果回到主线程更新 UI
  25.         dispatch_async(dispatch_get_main_queue(), ^{
  26.             [self updateUIWithProcessedData];
  27.         });
  28.     });
  29. }
复制代码
5. 自动引用计数 (ARC) 系统

自动引用计数是 Apple 在 iOS 5 中引入的内存管理机制,它通过自动插入内存管理代码,解放开辟者从手动管理内存的负担中。在 ARC 之前,开辟者需要依照严酷的"拥有权"规则,手动调用 retain 和 release 方法,这不但增加了开辟难度,也是潜伏错误的重要泉源。
ARC 的核心机制是通过编译器自动在适当位置插入内存管理代码,使对象的生命周期能够得到精确控制。与垃圾回收差别,ARC 在编译时确定对象的生命周期,因此没有运行时的性能开销。
5.1 编译器辅助内存管理

ARC 实现原理

ARC 的核心实现依赖于编译器的静态分析本领。LLVM 编译器会分析代码中的对象生命周期,自动在适当位置插入 retain、release 和 autorelease 调用。这一过程重要发生在编译阶段,详细步骤如下:

  • 引用计数增加场景:对象创建、赋值操作、方法参数传递
  • 引用计数淘汰场景:变量超出作用域、对象被重新赋值、显式设置为 nil
  1. // ARC 下的代码
  2. UIImage *image = [UIImage imageNamed:@"example"];
  3. self.imageView.image = image;
  4. image = nil;
  5. // 编译器转换后的实际代码(伪代码)
  6. UIImage *image = [[UIImage imageNamed:@"example"] retain];
  7. [self.imageView setImage:image];
  8. [image release];
  9. image = nil;
复制代码
编译时优化与安全措施

LLVM 编译器不但仅是简单地插入内存管理代码,还会实行多种优化:

  • 冗余引用计数操作消除:移除不必要的 retain/release 对
  • 快速路径优化:对常见模式举行特别处置惩罚
  • 静态分析检查:辨认潜伏的内存管理题目
运行时支持根本办法

虽然 ARC 的重要工作在编译时完成,但它仍然依赖 Objective-C 运行时提供的引用计数根本办法:

  • 引用计数存储:对象的引用计数存储在对象的内部或外部表中
  • SideTable 数据布局:在多线程环境下提供原子操作支持
  • 自动释放池:管理临时对象的生命周期
关键的运行时函数包括:
  1. // 核心内存管理函数
  2. id objc_retain(id obj);
  3. void objc_release(id obj);
  4. id objc_autorelease(id obj);
复制代码
5.2 ARC 修饰符与属性特性

明确 __strong、__weak、__unsafe_unretained、__autoreleasing

ARC 引入了一系列全部权修饰符,用于控制对象引用的举动:

  • __strong:默认修饰符,创建强引用,保持对象存活
  • __weak:创建弱引用,不增加引用计数,对象释放时自动置为 nil
  • __unsafe_unretained:创建不安全的非拥有引用,不增加引用计数,对象释放后变成悬挂指针
  • __autoreleasing:用于标记传递给方法的 out 参数,自动添加到当前自动释放池
  1. // 强引用(默认)
  2. __strong UIViewController *strongVC = [[UIViewController alloc] init];
  3. // 弱引用,避免循环引用
  4. __weak UIViewController *weakVC = strongVC;
  5. // 不安全的非拥有引用
  6. __unsafe_unretained UIViewController *unsafeVC = strongVC;
  7. // 自动释放引用(通常用于指针的指针)
  8. NSError *__autoreleasing *error = &errorPtr;
复制代码
属性举动:strong、weak、copy、assign

在属性声明中,我们可以使用差别的内存管理属性:

  • strong:默认属性,创建强引用,适用于大多数对象
  • weak:创建弱引用,适用于委托、父-子关系等场景
  • copy:在赋值时创建对象的副本,适用于不可变对象(如 NSString)
  • assign:简单赋值,不涉及引用计数,适用于基本数据范例和 unsafe_unretained 对象
  1. @interface ProfileViewController : UIViewController
  2. // 强引用属性(默认)
  3. @property (strong, nonatomic) UIImageView *avatarImageView;
  4. // 弱引用属性,避免循环引用
  5. @property (weak, nonatomic) id<ProfileViewControllerDelegate> delegate;
  6. // 复制属性,确保属性的不可变性
  7. @property (copy, nonatomic) NSString *username;
  8. // 简单赋值,用于基本数据类型
  9. @property (assign, nonatomic) NSInteger userAge;
  10. @end
复制代码
5.3 弱引用表实现与归零机制

弱引用(__weak)是 ARC 一个重要特性,能够制止循环引用题目。实在现依赖于全局弱引用表:

  • SideTable 布局:管理弱引用的散列表
  • 弱引用注册:当创建弱引用时,将其注册到全局表中
  • 引用归零:当对象释放时,运行时系统扫描弱引用表,将全部指向该对象的弱引用置为 nil

5.3 防止引用循环

引用循环是 ARC 下最常见的内存泄漏原因。以下是防止引用循环的策略:

  • 使用弱引用:在父-子关系中,子对象应持有父对象的弱引用
  • 合理使用 block:在 block 中捕捉 self 时使用弱引用
  • 委托模式:委托对象应该使用弱引用持有其委托
  • NSTimer 的特别处置惩罚:使用中心对象或 block-based API
  1. // 在 block 中避免引用循环
  2. __weak typeof(self) weakSelf = self;
  3. self.completionHandler = ^{
  4.     __strong typeof(weakSelf) strongSelf = weakSelf;
  5.     if (strongSelf) {
  6.         [strongSelf processData];
  7.     }
  8. };
  9. // 避免 NSTimer 引用循环
  10. // 不推荐的做法
  11. [NSTimer scheduledTimerWithTimeInterval:1.0
  12.                                  target:self
  13.                                selector:@selector(timerFired:)
  14.                                userInfo:nil
  15.                                 repeats:YES];
  16. // 推荐的做法 (iOS 10+)
  17. self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0
  18.                                              repeats:YES
  19.                                                block:^(NSTimer * _Nonnull timer) {
  20.     __strong typeof(weakSelf) strongSelf = weakSelf;
  21.     [strongSelf timerFired:timer];
  22. }];
复制代码
6. 高级内存管理与优化

6.1 高性能内存模式

自动释放池设计与优化

自动释放池(Autorelease Pool)是 Objective-C 内存管理的重要部分,即使在 ARC 下依然广泛使用:

  • 基本原理:延迟对象的释放,直到当前池被销毁
  • 嵌套布局:自动释放池可以嵌套,形成栈式布局
  • 线程相关性:每个线程都有自己的自动释放池栈
  1. // 自定义自动释放池,减少内存峰值
  2. @autoreleasepool {
  3.     for (int i = 0; i < 100000; i++) {
  4.         NSString *string = [NSString stringWithFormat:@"String %d", i];
  5.         // 使用 string
  6.     }
  7. } // 池结束,临时对象立即释放
复制代码
临时对象与快速释放路径

为了进步性能,ARC 引入了快速释放路径(Fast Release Path)等优化:

  • 直接释放:当对象的引用计数降至零时直接释放,而非加入自动释放池
  • 局部变量优化:编译器尝试在函数作用域竣事时释放局部变量,而非使用自动释放
  • return 值优化:对于返回自动释放对象的方法,可以使用 os_retainAutoreleasedReturnValue/os_autoreleasedReturnValue 优化

延迟释放与批量回收技术

在高性能 iOS 应用中,内存管理常常需要衡量即时性和服从:

  • 批量回收:自动释放池允许批量回收多个对象,进步服从
  • 多线程考虑:背景线程中创建大量对象时,使用自动释放池控制内存占用
  • 图像处置惩罚优化:处置惩罚大型图像时,及时释放中心缓冲区
  1. // 后台线程处理大量数据
  2. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  3.     @autoreleasepool {
  4.         // 处理大量数据
  5.         NSArray *largeArray = [self processLargeDataSet];
  6.         
  7.         // 进一步处理
  8.         NSArray *results = [self furtherProcessArray:largeArray];
  9.         
  10.         // 回到主线程更新 UI
  11.         dispatch_async(dispatch_get_main_queue(), ^{
  12.             [self updateUIWithResults:results];
  13.         });
  14.     } // 自动释放池结束,临时对象释放
  15. });
复制代码
内存告诫响应框架

iOS 系统会在内存压力大时发送内存告诫,应用应当及时响应:

  • UIApplicationDidReceiveMemoryWarningNotification:注册通知监听内存告诫
  • didReceiveMemoryWarning:UIViewController 方法,用于响应内存告诫
  • 缓存清算:实现缓存系统,能够在内存告诫时清算非必要资源
  1. // 在视图控制器中响应内存警告
  2. - (void)didReceiveMemoryWarning {
  3.     [super didReceiveMemoryWarning];
  4.    
  5.     // 清理可重建的资源
  6.     [self.imageCache removeAllObjects];
  7.    
  8.     // 释放离屏渲染的资源
  9.     [self.offscreenRenderingBuffer releaseResources];
  10.    
  11.     // 移除不可见视图控制器
  12.     if (![self isViewLoaded] || !self.view.window) {
  13.         [self.view removeFromSuperview];
  14.         self.view = nil;
  15.     }
  16. }
复制代码
6.2 诊断与优化技术

内存泄漏检测方法

内存泄漏是 iOS 应用中常见的题目,有多种方法可以检测:

  • Instruments 的 Leaks 工具:辨认未被释放的对象
  • Xcode 内存调试器:可视化显示对象间的引用关系
  • 堆分析:跟踪堆上分配的内存,分析增长趋势
循环引用分析工具

循环引用(retain cycle)是内存泄漏的重要原因之一:

  • Xcode 内存图:可视化查看对象之间的引用关系
  • FBRetainCycleDetector:Facebook 开源的运行时检测工具
  • 静态分析器:在编译时检测潜伏的循环引用题目
  1. // 使用 Facebook 的 FBRetainCycleDetector 工具检测循环引用
  2. #import <FBRetainCycleDetector/FBRetainCycleDetector.h>
  3. FBRetainCycleDetector *detector = [FBRetainCycleDetector new];
  4. [detector addCandidate:self];
  5. NSSet *retainCycles = [detector findRetainCycles];
  6. if ([retainCycles count] > 0) {
  7.     NSLog(@"Found retain cycles: %@", retainCycles);
  8. }
复制代码
大型内存分配观察

当应用举行大量内存分配时,需要特别关注:

  • 分配的泉源:使用 Instruments 的 Allocations 工具跟踪
  • 适当缓存策略:制止重复分配大块内存
  • 大图像处置惩罚技术:按需降采样、分片处置惩罚
  1. // 按需处理大型图像
  2. - (UIImage *)downsampledImageFromURL:(NSURL *)imageURL toSize:(CGSize)targetSize {
  3.     NSDictionary *options = @{
  4.         NSURLCacheStorageAllowed: @NO
  5.     };
  6.    
  7.     NSData *imageData = [NSData dataWithContentsOfURL:imageURL options:0 error:nil];
  8.    
  9.     // 创建 CGImageSource
  10.     CGImageSourceRef imageSource = CGImageSourceCreateWithData((__bridge CFDataRef)imageData, NULL);
  11.    
  12.     // 设置降采样选项
  13.     NSDictionary *downsampleOptions = @{
  14.         (NSString *)kCGImageSourceCreateThumbnailFromImageAlways: @YES,
  15.         (NSString *)kCGImageSourceThumbnailMaxPixelSize: @(MAX(targetSize.width, targetSize.height) * [UIScreen mainScreen].scale),
  16.         (NSString *)kCGImageSourceCreateThumbnailWithTransform: @YES,
  17.         (NSString *)kCGImageSourceShouldCacheImmediately: @YES
  18.     };
  19.    
  20.     // 创建降采样图像
  21.     CGImageRef downsampledImage = CGImageSourceCreateThumbnailAtIndex(imageSource, 0, (__bridge CFDictionaryRef)downsampleOptions);
  22.     UIImage *resultImage = [UIImage imageWithCGImage:downsampledImage];
  23.    
  24.     // 释放资源
  25.     CGImageRelease(downsampledImage);
  26.     CFRelease(imageSource);
  27.    
  28.     return resultImage;
  29. }
复制代码
低内存处置惩罚策略

在低内存条件下,应用应当优雅地释放资源:

  • 优先级系统:对缓存和资源建立优先级系统
  • 分级响应:根据内存压力水平采取差别的措施
  • 预防机制:制止达到系统逼迫停止的临界点
  1. // 实现分级内存清理系统
  2. - (void)setupMemoryHandling {
  3.     // 注册内存警告通知
  4.     [[NSNotificationCenter defaultCenter] addObserver:self
  5.                                              selector:@selector(handleMemoryWarning:)
  6.                                                  name:UIApplicationDidReceiveMemoryWarningNotification
  7.                                                object:nil];
  8. }
  9. - (void)handleMemoryWarning:(NSNotification *)notification {
  10.     // 确定当前内存压力等级
  11.     UIApplicationState state = [UIApplication sharedApplication].applicationState;
  12.     BOOL isBackground = (state == UIApplicationStateBackground);
  13.    
  14.     // 第一级:清理低优先级缓存
  15.     [self.imageCache clearLowPriorityItems];
  16.    
  17.     // 第二级:如果在后台,清理更多资源
  18.     if (isBackground) {
  19.         [self.imageCache clearAllExceptVisible];
  20.         [self cancelNonEssentialOperations];
  21.     }
  22.    
  23.     // 第三级:极端情况,释放几乎所有可重建的资源
  24.     if (self.systemMemoryPressureExtreme) {
  25.         [self.imageCache clearAll];
  26.         [self purgeViewControllers];
  27.         [self resetToMinimalState];
  28.     }
  29. }
复制代码
背景应用的内存压力适配

iOS 对背景应用的内存限制更为严酷,需要特别处置惩罚:

  • 背景模式优化:进入背景时主动释放资源
  • 背景革新策略:使用背景使命 API 时留意内存使用
  • 背景使命完成及时通知:制止系统停止使命
  1. // 应用进入后台时释放资源
  2. - (void)applicationDidEnterBackground:(UIApplication *)application {
  3.     // 保存必要状态
  4.     [self saveApplicationState];
  5.    
  6.     // 释放大型资源
  7.     [self.mediaCache purgeNonEssentialResources];
  8.    
  9.     // 释放视图资源
  10.     [self cleanupViewHierarchy];
  11.    
  12.     // 通知完成背景任务
  13.     UIBackgroundTaskIdentifier taskID = self.backgroundTaskID;
  14.     if (taskID != UIBackgroundTaskInvalid) {
  15.         [application endBackgroundTask:taskID];
  16.         self.backgroundTaskID = UIBackgroundTaskInvalid;
  17.     }
  18. }
复制代码
结论

iOS 内存管理已经从最初的手动管理模式发展到如今的 ARC 自动管理机制,极大进步了开辟服从并淘汰了内存相关错误。然而,高性能 iOS 应用仍然需要开辟者深入明确内存管理机制,制止循环引用和内存泄漏,优化内存占用,并合理处置惩罚内存压力。
通过合理使用自动释放池、弱引用、适当的缓存策略以及内存监控工具,你将能够在保持应用性能的同时,提供流畅的用户体验,制止因内存题目导致的瓦解和性能降落。。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

水军大提督

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表