ARC、AutoreleasePool、Thread、RunLoop之间的关系

一、内存管理五大区

 

在iOS中内存主要分为五大区域:栈区、堆区、静态区、常量区、代码区。

栈区与堆区对比

  • 栈是一段连续的内存区域,堆是不连续的内存。
  • 栈系统自动回收内存,堆需要开发人员手动释放。
  • 栈内存大小有限制,内存空间小,堆内存空间大。

二、ARC的内部实现 

当ARC开启时,编译器将自动在代码合适的地方插入retain, release和autorelease。

ARC主要依赖于这三个方法:

  • retain 增加引用计数
  • release 降低引用计数,引用计数为0的时候,释放对象。
  • autorelease 在当前的auto release pool结束后,降低引用计数。
- (id)retain {
    return ((id)self)->rootRetain();
}
inline id objc_object::rootRetain()
{
    if (isTaggedPointer()) return (id)this;
    return sidetable_retain();
}
id objc_object::sidetable_retain()
{
    //获取table
    SideTable& table = SideTables()[this];
    //加锁
    table.lock();
    //获取引用计数
    size_t& refcntStorage = table.refcnts[this];
    if (! (refcntStorage & SIDE_TABLE_RC_PINNED)) {
         //增加引用计数
        refcntStorage += SIDE_TABLE_RC_ONE;
    }
    //解锁
    table.unlock();
    return (id)this;
}

struct SideTable {
    spinlock_t slock;
    RefcountMap refcnts;
    weak_table_t weak_table;
     //省略其他实现...
};
//默认所有对象的修饰符都是__strong。
{
    id __strong obj = [NSObject alloc] init];   //引用计数+1
}   // obj 超过作用域,强引用失效,将会自动释放所持有的对象

1.以alloc,copy,mutableCopy和new这些方法创建的会被默认标记 __attribute((ns_returns_retained))  自动retain ,超作用域释放
注解:在ARC下,返回值为对象的方法或者函数时,将会在函数return之前将返回值retain一次,这样的函数或者方法接收到返回值retain时,ARC会在其包含得完整表达式结尾处释放该值。
2.而不以这些关键字开头的方法会被标记__attribute((ns_returns_retained)) 
创建的对象就会被注册到自动释放池中(则通过objc_autoreleaseReturnValue和objc_retainAutoreleaseReturnValue来判断是否需要加入到autoreleasepool),同时其释放会延迟,等到自动释放池释放的时候才会销毁。

id指针(id *)和对象指针(如NSError**),会自动加上关键字__autoreleasing,加入到autoreleasepool。

使用容器的block版本的枚举器时,内部会自动添加一个autoreleasepool:

[array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
    // 这里被一个局部@autoreleasepool包围着
}];

当然,在普通for循环和for in循环中没有,for循环中遍历会产生大量autorelease变量时,就需要手动添加局部autoreleasepool。

三、RunLoop、NSThread、Autoreleasepool关系

App启动后,苹果在主线程RunLoop里注册了两个Observer,其回调都是_wrapRunLoopWithAutoreleasePoolHandler()。

  • 第一个Observer监视的事件是Entry(即将进入Loop),其回调内会调用_objc_autoreleasePoolPush()创建自动释放池。其order是-214783647,优先级最高,保证创建释放池发生在其他所有回调之前。
  • 第二个Observer监视了两个事件:BeforeWaiting(准备进入休眠)时调用_objc_autoreleasePoolPop()和_objc_autoreleasePoolPush()释放旧的池并创建新池;Exit(即将退出Loop)时调用_objc_autoreleasePoolPop()来释放自动释放池。这个Observer的order是214783647,优先级最低,保证其释放池子发生在其他所有回调之后。

1.RunLoopNSThread(线程):

  1. RunLoop与线程是一一对应关系,每个线程(包括主线程)都有一个对应的RunLoop对象;其对应关系保存在一个全局的Dictionary里;
  2. 主线程的RunLoop默认由系统自动创建并启动;而其他线程在创建时并没有RunLoop,若该线程一直不主动获取,就一直不会有RunLoop
  3. 苹果不提供直接创建RunLoop的方法;所谓其他线程Runloop的创建其实是发生在第一次获取的时候,系统判断当前线程没有RunLoop就会自动创建;
  4. 当前线程结束时,其对应的Runloop也被销毁;

2.Runloop与Autoreleasepool

  • 从程序启动到加载完成,主线程对应的 Runloop 会处于休眠状态,等待用户交互来唤醒 Runloop
  • 用户每次交互都会启动一次 Runloop ,用于处理用户的所有点击、触摸等事件
  • Runloop 在监听到交互事件后,就会创建自动释放池,并将所有延迟释放的对象添加到自动释放池中
  • 在一次完整的 Runloop 结束之前,会向自动释放池中所有对象发送 release 消息,然后销毁自动释放池

3.线程与Autoreleasepool

所有线程都维护有它自己的自动释放池的堆栈结构。新的自动释放池被创建的时候,它们会被添加到栈的顶部,而当池子销毁的时候,会从栈移除。对于当前线程来说,Autoreleased对象会被放到栈顶的自动释放池中。当一个线程线程停止,它会自动释放掉与其关联的所有自动释放池。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

iOS KunPeng

一天写不出一片好文章,感谢!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值