IOS开发入门iOS 自动释放池ARC与MRC
白羽 2019-07-10 来源 :网络 阅读 1064 评论 0

摘要:本文将带你了解IOS开发入门iOS 自动释放池ARC与MRC,希望本文对大家学IOS有所帮助。

    本文将带你了解IOS开发入门iOS 自动释放池ARC与MRC,希望本文对大家学IOS有所帮助。

IOS开发入门iOS 自动释放池ARC与MRC

自动释放池是oc提供的一种自动回收的机制,具有延迟释放的特性,即当我们创建了一个对象,并把他加入到了自动释放池中时,他不会立即被释放,会等到一次runloop结束或者作用域超出{}或者超出[pool  release]之后再被释放
   
   自动释放池的创建与销毁时机
   MRC
   1:  通过手动创建的方式来创建自动释放池,这种方式创建的自动释放池需要手动调用release(引用计数环境下,调用release和drain的效果相同,但是在CG下,drain会触发GC,release不会),方法如下:
   NSAutoreleasePool *pool = [[ NSAutoreleasePool alloc]init  ];//创建一个自动释放池Person *person = [[Person  alloc]init];//调autorelease方法将对象加入到自动释放池//注意使用该方法,对象不会自己加入到自动释放池,需要人为调用autorelease方法加入[person  autorelease];//,手动释放自动释放池执行完这行代码是,自动释放池会对加入他中的对象做一次release操作[pool  release];
   自动释放池销毁时机:[pool release]代码执行完后.
   2: 通过@autoreleasepool来创建
   1: 对象的创建在自动释放池里面
   @autoreleasepool  {       //在这个{}之内的变量默认被添加到自动释放池        Person  *p = [[Person alloc] init];     }//除了这个括号,p被释放
   2: 如果一个变量在自动释放池之外创建,如下,需要通过__autoreleasing该修饰符将其加入到自动释放池。
   @autoreleasepool { }Person *   __autoreleasing p = [[Person  alloc]init]; self.person = p;
   系统就是通过@autoreleasepool  {}这种方式来为我们创建自动释放池的,一个线程对应一个runloop,系统会为每一个runloop隐式的创建一个自动释放池,所有的autoreleasePool构成一个栈式结构,在每个runloop结束时,当前栈顶的autoreleasePool会被销毁,而且会对其中的每一个对象做一次release(严格来说,是你对这个对象做了几次autorelease就会做几次release,不一定是一次),特别指出,使用容器的block版本的枚举器的时候,系统会自动添加一个autoreleasePool
   [array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop)  {
   // 这里被一个局部@autoreleasepool包围着
   }];
   ARC
   
    ARC下除了NSAutoreleasePool不可用以外,其他的同MRC
   
   自动释放池的原理
   在了解自动释放池的原理之前,我们先来看一下以下几个概念
   AutoreleasePoolPage:每一个自动释放池没有单独的结构,每一个autorealeasePool对象都是由若干个autoreleasePoolPage通过双向链表连接而成,类的定义如下
   class AutoreleasePoolPage {    magic_t  const magic;    id  *next;//指向栈顶最新被添加进来的autorelease对象的下一个位置    pthread_t  const thread;//指向当前线程    AutoreleasePoolPage * const  parent;    AutoreleasePoolPage *child;    uint32_t  const depth;    uint32_t   }
   
    autoreleasePoolPage是按照线程一一对应的,  autoreleasePoolPage会开辟4096字节空间,除了上面的实例变量所占的空间,剩余的空间全部用来存储autorelease对象的地址 id  *next:指向栈顶最新被添加进来的autorelease对象的下一个位置  一个autoreleasePoolPage空间被占满时,会创建一个新的autoreleasePoolPage对象,后来的对象添加在在新
   
    autoreleasePoolPage中
   哨兵对象:本质是一个值为nil的对象,由被定义在* NSAutoreleasePool类中的_token指针来保存。
   hotPage:最新创建的AutoreleasePoolPage.
   系统通过一个栈来管理所有的自动释放池,每当创建了一个新的自动释放池,系统就会把它压入栈顶,并且传入一个哨兵对象,将哨兵对象插入hotPage,这里分三种情况
   若hotPage未满,则直接插入哨兵对象, 要是满了,新建一个NSAutoreleasePoolPage,并将其作为hotPage,然后将哨兵对象插入  如果没有NSAutoreleasePoolPage,则新建一个NSAutoreleasePoolPage,并将其作为hotPage,插入哨兵对象,注意。这里的hotPage是没有父节点的。  
   每当有一个自动释放池要被释放的时候,哨兵对象就会作为参数被传入,找到该哨兵对象所在的位置后,将所有晚于哨兵对象的autorelease弹出,并对他们做一次release,然后将next指针一到合适的位置。
   自动释放池的应用场景
   
    MRC:
    
   
    对象作为函数返回值
    当一个对象要作为函数返回值的时候,因为要遵循谁申请谁释放的思想,所以应该在返回之前释放,但要是返回之前释放了,就会造成野指针错误,但是要是不释放,那么就违背了谁申请谁释放的原则,所以就可以使用autorelease延迟释放的特性,将其在返回之前做一次autorelease,加入到自动释放池中,保证可以被返回,一次runloop之>>后系统会帮我们释放他  临时生成大量对象,一定要将自动释放池放在for循环里面,要释放在外面,就会因为大量对象得不到及时释放,而造成内存紧张,最后程序意外退出
   
    for (int i = 0; i<10000; i++) {        @autoreleasepool  {            UIImageView  *imegeV = [[UIImageView  alloc]init];            imegeV.image  = [UIImage  imageNamed:@"efef"];            [self.view  addSubview:imegeV];        }    }
   
   嵌套的autoreleasepool
   @autoreleasepool  {//p1被放在该自动释放池里面        Person *p1 =  [[Person  alloc]init];        @autoreleasepool  {//p2被放在该自动释放池里面            Person  *p2 = [[Person  alloc]init];        }    }
   以上说明autorelease对象被添加进离他最近的自动释放池,多层的pool会有多个哨兵对象。
   ARC下autorelease的优化
   ARC下,runtime提供了一套对autorelease返回值的优化策略TLS(线程局部存储),就是为每个线程单独分配一块栈控件。以key-value的形式对数据进行存储(ARC对autorelease对象的优化标记)。
   1:先看优化中涉及到的几个函数
   __builtin_return_address(int level)
   
    是一个内建函数,作用是返回函数的地址,参数是层级,如果是0,则表示是当前函数体返回地址;如果是1:则表示调用这个函数的外层函数。当我们知道了一个函数体的返回地址的时候,就可以根据反汇编,利用某些固定的偏移量,被调方可以定位到主调放在返回值后>面的汇编指令,来确定该条函数指令调用完成后下一个调用的函数
   
   objc_autoreleaseReturnValue
   
    通过__builtin_return_address(int level)检测外部是不是ARC环境,可以替代autorelease,是的话直接返回object,不是的话调用objc_autorelease()将对象注册到自动释放池里面,最后通过objc_retain来获取对象。
   
   objc_retainAutoreleasedReturnValue
   通过以上代码可以看出,
   
    与objc_autoreleaseReturnValue配合使用,如果在执行完objc_autoreleaseReturnValue()这个函数的下一个调用函数是objc_retainAutoreleasedReturnValue,那么就走最优化(在TLS中查询关于这个对象,如果有,直接返回对象,不再对对象做retain)。
   2:过程
   新建Person类 Person.h
  #import @interface Person :  NSObject+(instancetype)createPerson; @end
   Person.m
   #import "Person.h" @implementation  Person +(instancetype)createPerson{    return [self  new];} @end
   ViewController.m
   #import  "ViewController.h"#import "Person.h"@interface  ViewController ()@end @implementation ViewController -  (void)viewDidLoad {    [super viewDidLoad];   [self  testPerson];} - (void)TestPerson {    self.person =  [Person createPerson];}
   简化后对应的汇编:
   + (instancetype)createPerson  {    id tmp = [self new];       return objc_autoreleaseReturnValue(tmp); } -  (void)testFoo {    id tmp =  _objc_retainAutoreleasedReturnValue([Person createPerson]);   }
   在调用objc_autoreleaseReturnValue的时候,会先通过__builtin_return_address这个内建函数return  address,然后根据这个地址判断主调方在调用完objc_autoreleaseReturnValue这个函数以后是否紧接着调用了objc_retainAutoreleasedReturnValue函数,如果是,那么objc_autoreleaseReturnValue()就不将返回的对象注册到自动释放池里面(不做autorelease),runtime会将这个返回值存储在TLS中,做一个优化标记,然后直接返回这个对象给函数的调用方,在外部接收这个返回值的objc_retainAutoreleasedReturnValue()会先在TLS中查询有没有这个对象,如果有,那么就直接返回这个对象(不调用retain),所以通过objc_autoreleaseReturnValue和objc_retainAutoreleasedReturnValue的相互配合,利用TSL做一个中转,在ARC下省去了autorelease和retain的步骤,在一定程度上达到了最优化. 

 

本文由职坐标整理并发布,希望对同学们有所帮助。了解更多详情请关注职坐标移动开发之IOS频道!

本文由 @白羽 发布于职坐标。未经许可,禁止转载。
喜欢 | 0 不喜欢 | 0
看完这篇文章有何感觉?已经有0人表态,0%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    ICP许可  沪B2-20190160

©2015 www.zhizuobiao.com All Rights Reserved

208小时内训课程