AutoReleasePool 底层原理

本文深入探讨了Objective-C中的AutoReleasePool概念,包括它在RunLoop中的作用,以及通过clang编译后的cpp实现和底层的内存管理原理。文章通过汇编分析展示了AutoReleasePool的push和pop操作,解释了如何存储和释放对象,同时揭示了其与内存页的关系。

概念

AutoreleasePool(自动释放池)OC中的一种内存自动回收机制,它可以延迟加入AutoreleasePool中的变量release的时机。在正常情况下,创建的变量会在超出其作用域的时候release,但是如果将变量加入AutoreleasePool,那么release延迟执行。
2251862-00621f50ede334d6.png

  • App启动后,苹果在主线程 RunLoop里注册了两个 Observer,其回调都是 _wrapRunLoopWithAutoreleasePoolHandler(),从程序启动到加载完成,主线程对应的runloop会处于休眠状态,等待用户交互唤醒runloop
    • 第一个 Observer 监视的事件是 Entry(即将进入Loop),其回调内会调用 _objc_autoreleasePoolPush()创建自动释放池。优先级最高,保证创建释放池发生在其他所有回调之前
    • 第二个 Observer监视了两个事件:BeforeWaiting(准备进入休眠) 时调用_objc_autoreleasePoolPop()_objc_autoreleasePoolPush()释放旧的池并创建新池;Exit(即将退出Loop) 时调用 _objc_autoreleasePoolPop()来释放自动释放池。优先级最低,保证其释放池子发生在其他所有回调之后
  • 用户的每一次交互都会启动一次runloop,用于处理用户的所有点击、触摸事件
  • runloop在监听到交互事件后,就会创建自动释放池,并将所有延迟释放的对象添加到自动释放池中
  • 主线程执行的代码,通常是写在诸如事件回调、Timer回调内的。这些回调会被RunLoop创建好的 AutoreleasePool环绕着,所以不会出现内存泄漏,开发者也不必显示创建 Pool了。
  • 在一次完整的runloop结束之前,会向自动释放池中所有对象发送release消息,然后销毁自动释放池

clang 分析

int main(int argc, char * argv[]) {
    @autoreleasepool {
    }
}

通过clang编译成cpp文件插看实现:
xcrun -sdk iphonesimulator clang -arch x86_64 -rewrite-objc main.m

int main(int argc, char * argv[]) {
    /* @autoreleasepool */ {
          __AtAutoreleasePool __autoreleasepool; 
    }
}

struct __AtAutoreleasePool {
  __AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();}
  ~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj);}
  void * atautoreleasepoolobj;
};

通过代码可以看出autoreleasepool在底层实际是调用__AtAutoreleasePool,而__AtAutoreleasePool本质上是一个结构体,其内部包含构造函数__AtAutoreleasePool()析构函数~__AtAutoreleasePool(),在{}作用域结束后会自动调用析构函数,以便及时创建销毁

###汇编分析

struct LGTest {
    LGTest(){
        printf("1123 - %s",__func__);
    }
    ~LGTest(){
        printf("5667 - %s",__func__);
    }
};

int main(int argc, char * argv[]) {
         LGTest LGTest; 
}

在main函数中添加断点查看汇编
2251862-6b5a98298a8d11c5.jpg

可以看出跟clang编译后一样都是经过objc_autoreleasePoolPushobjc_autoreleasePoolPop

底层原理

在objc源码中是这样注释的

Autorelease pool implementation

- A thread's autorelease pool is a stack of pointers. 
线程的自动释放池是指针的堆栈

- Each pointer is either an object to release, or POOL_BOUNDARY which is an autorelease pool boundary.
每个指针都是要释放的对象,或者是POOL_BOUNDARY(哨兵),它是自动释放池的边界。

- A pool token is a pointer to the POOL_BOUNDARY for that pool. When the pool is popped, every object hotter than the sentinel is released.
池令牌是指向该池的POOL_BOUNDARY的指针。弹出池后,将释放比哨点更热的每个对象。

- The stack is divided into a doubly-linked list of pages. Pages are added and deleted as necessary. 
堆栈分为两个双向链接的页面列表。根据需要添加和删除页面。

- Thread-local storage points to the hot page, where newly autoreleased objects are stored. 
线程本地存储指向热页面,该页面存储新自动释放的对象。

查看源码

void *
objc_autoreleasePoolPush(void)
{
    return AutoreleasePoolPage::push();
}

NEVER_INLINE
void
objc_autoreleasePoolPop(void *ctxt)
{
    AutoreleasePoolPage::pop(ctxt);
}

通过代码可以看出pushpop操作都是基于AutoreleasePoolPage,根据其定义看出自动释放池是页结构,每页的大小为4096字节

//************宏定义************
#define PAGE_MIN_SIZE           PAGE_SIZE
#define PAGE_SIZE               I386_PGBYTES
#define I386_PGBYTES            4096            /* bytes per 80386 page */

//************类定义************
class AutoreleasePoolPage : private AutoreleasePoolPageData
{
    friend struct thread_
<think>好的,用户让我介绍一下iOS的内存管理及其底层原理。我需要先回忆一下相关知识,确保信息准确。首先,iOS的内存管理主要依赖自动引用计数(ARC),这点是基础。然后,得解释ARC是怎么工作的,比如引用计数的增减,对象的持有和释放。可能还需要提到MRC,也就是手动管理时代的情况,作为对比,突出ARC的优势。 接下来,用户可能想知道底层原理,所以需要深入一点。比如引用计数是如何存储的,是使用SideTable还是其他结构。这里要确认一下,SideTable在iOS中的具体实现,是否有多个SideTable来减少锁竞争。还有weak指针的实现,比如如何自动置nil,避免野指针。 另外,内存管理中的常见问题,比如循环引用,需要举例说明,比如block中的self使用,这时候应该提到__weak修饰符。还有autoreleasepool的作用,比如在循环中大量创建临时对象时,如何利用autoreleasepool及时释放内存。 可能还需要提到内存管理的其他方面,比如内存警告的处理,ViewController的didReceiveMemoryWarning方法,开发者需要在这里释放不必要的资源。还有内存的分区,比如栈、堆、全局区等,但用户可能更关注ARC和底层机制,这部分可以简要带过。 需要检查是否有最新的信息,比如Swift中的内存管理是否有所不同,但题目是iOS,可能包括OC和Swift,不过ARC在两者中都是基础。不过Swift使用了更简洁的语法,比如strong、weak、unowned,可能需要简单提及。 还要注意结构清晰,分点回答,比如分ARC原理底层机制、常见问题、优化策略等部分。同时要使用用户要求的格式,数学公式用$...$,独立公式用$$...$$。例如,引用计数的变化可以用数学表达式表示,比如引用计数加1减1的操作。 可能存在的疑问点:SideTable的具体结构,比如引用计数和weak表是否真的存在散列表中,需要确认资料来源是否准确。此外,Tagged Pointer的概念,是否属于内存管理的一部分,比如小对象的存储优化,避免内存分配,这也是iOS内存管理的一个优化点,应该包含进去。 最后,确保语言通俗易懂,避免过于晦涩的技术术语,但又要准确。比如解释weak指针时,可以说明它们如何不增加引用计数,并在对象释放时自动置nil,防止悬挂指针。 总结一下,回答的结构大致分为几个部分:ARC的基本原理底层实现细节(引用计数存储、SideTable、weak机制),常见问题及解决方法(循环引用、内存泄漏),以及优化技巧(如使用autoreleasepool、处理内存警告)。同时,要确保符合格式要求,正确使用LaTeX,特别是引用计数的数学表达式可能需要用$...$包裹。</think>我将从iOS内存管理核心机制、底层原理、常见问题及优化策略三个层次进行解析: $$内存管理模型 = 自动引用计数(ARC) + 底层对象生命周期控制$$ 一、核心机制:自动引用计数(ARC) 1. 基本工作原理 - 每个对象内置引用计数器:$RC_{obj}$ - 当$RC_{obj} > 0$时对象存活,$RC_{obj} = 0$时立即释放 - 操作示例: $$[obj retain] \Rightarrow RC_{obj} += 1$$ $$[obj release] \Rightarrow RC_{obj} -= 1$$ 2. 所有权修饰符(Swift对应概念) | 修饰符 | 作用特性 | 使用场景 | |------------|----------------------------|------------------------| | __strong | 默认修饰符,增加RC | 常规对象持有 | | __weak | 不增加RC,自动nil化 | 打破循环引用 | | __unsafe_unretained | 不增加RC,不自动nil化 | 性能敏感场景(需确保生命周期)| | __autoreleasing | 延迟释放 | 方法参数传递 | 二、底层实现原理 1. 引用计数存储优化 - Tagged Pointer:小对象直接编码在指针中,无真实内存分配 $$Pointer_{tagged} = 标志位(1bit) + 数据类型(3bits) + 有效载荷(60bits)$$ - Non-pointer isa:64位系统中利用isa指针冗余位存储RC - SideTable散列表:当RC溢出时转存至分离结构 2. SideTable结构(线程安全实现) ```c++ struct SideTable { spinlock_t lock; // 自旋锁 RefcountMap refcnts; // 引用计数哈希表 weak_table_t weak_table; // weak指针登记表 }; ``` 系统维护全局SideTables(多个SideTable实例),通过对象指针哈希选择对应表,降低锁竞争 3. Weak机制实现 - 注册阶段:将weak指针地址记录到weak_table - 清除阶段:对象dealloc时遍历weak_table ① 将weak指针指向nil ② 从weak_table移除记录 ③ 具体操作: $$ \forall ptr \in weak\_table[obj], \quad *ptr = nil $$ 三、典型问题与优化 1. 循环引用场景 - 对象相互强引用:A.objB ↔ B.objA - 解决方案: ```objective-c __weak typeof(self) weakSelf = self; block = ^{ __strong typeof(weakSelf) strongSelf = weakSelf; [strongSelf doSomething]; }; ``` 2. 内存峰值优化 - AutoreleasePool合理使用: ```objective-c for (int i=0; i<100000; i++) { @autoreleasepool { NSString *temp = [NSString stringWithFormat:@"%d",i]; // 处理临时对象 } // 池结束时立即释放 } ``` 3. 内存警告处理 ```objective-c - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; [self clearCache]; // 主动释放可重建资源 [self reloadData]; // 必要时重建数据 } ``` 四、底层方法调用链 1. 对象释放过程: retainCount → release → dealloc → _objc_rootDealloc → object_dispose 2. 内存分配过程: alloc → _objc_rootAlloc → calloc → vm_allocate 性能监控建议: - 使用Xcode Memory Graph定位循环引用 - Instruments的Allocations工具分析内存分配模式 - VM Tracker检测内存压缩质量 通过理解这些机制,开发者可以更精准地控制内存使用,在保证应用流畅度的同时降低被系统终止的风险。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值