ios总结-内存管理篇

有关ios的内存管理多很熟悉,无论MRC手动内存管理,还是ARC自动内存管理都是采用的引用计数机制的内存管理方式.

ios内存管理中的四个关键字,alloc,new,copy,mutableCopy,创建一个对象并获取所有权引用计数为1,引用对象时,retain(MRC),strong(ARC),引用计数+1,放弃所有权release引用计数-1,当为0时,调用dealloc释放.

怎么理解引用计数机制?引用计数管理思想?

遵循"谁创建,谁释放"的原则

自己生成的,自己持有(alloc,new,copy,mutableCopy)

非自己生成的对象,自己也可以持有(retain,strong,copy)

不再需要自己持有的对象时就释放掉(release)

无法释放自己持有的对象

内存申请了必须归还

ARC的四种修饰符:__strong(默认修饰符,强引用,持有对象,计数+1,如需强制释放,可置nil,类似定时器)__weak(避免造成循环引用,不持有对象,引用对象被释放后,引用本身置nil,避免野指针)__unsafe_unretained(使用不多,会造成野指针)__autoreleasing(@autoreleasepool)

MRC有什么缺点?ARC有什么局限性?

使用MRC需要手动来管理内存,避免不了会产生内存管理,所以IOS5出来了ARC,编译器会自动检查方法名是否以alloc/new/copy/mytableCopy开始,不是则自动将返回对象注册到autoreleasepool.在编译的时候插入retain/release,而且ARC会借用底层C来提高性能.在ARC里,有时候需要我们自己去创建@autoreleasepool来释放

ARC的局限是ARC只能用在Objective-c对象上(集成来自于NSObject的对象),但涉及底层的Core Foundation中malloc(),free(),循环引用,需要手动管理.

不得不说下Autoreleasepool,当给一个对象发送autoreleasepool消息时,方法会在某个时间给这个对象执行release操作的.

autoreleasepool什么时间创建? 什么时间销毁?

系统创建的autoreleasepool的释放时间:线程和runloop时一对一关系,主线程主动创建runloop,子线程手动创建runloop时,当@autoreleasepool加入子线程时,会调用runloop的autorelease对象释放,当线程的runloop仅需休眠时销毁旧的,再创建新的.当线程退出时,就是创建的@autoreleasepool销毁的时间.

官方文档上说,ARC中,当有类似for循环创建大量临时变量,导致内存占用不断增长这样的,建议使用@autoreleasepool

自己创建的@autoreleasepool的释放时机

@autoreleasepool{}括号内的对象时时跟着自动执行release操作的,要搞清楚@autoreleasepool{}里面的对象是什么时候释放的,就只需要弄明白自己创建的@autoreleasepool是什么时候释放的?

@autoreleasepool是以栈的形式存储的,先进后出,主线程的@autoreleasepool是在runloop创建的,在最里面,自己创建的按照栈的规则顺序释放的.

苹果是如何实现autoreleasepool的?

在没有自己手加的@autoreleasepool的情况下,@autoreleasepool是在当前的runloop迭代结束时,释放的.释放的原因是系统在每个runloop迭代中都加入了自动释放池Push和Pop

ARC下,  @autoreleasepool编译器的显示 void *context = objc_autoreleasePoolPush();

//{}代码

objc_autoreleasePoolPop(context);

两个函数都是AutoreleasePoolPage的简单封装,自动释放机制的核心在这个类.

@autoreleasepool室友若干个AutoreleasePoolPage以双向链表形式组合的, id *next指向栈顶下一个的位置,thread是当前线程,AutoreleasePoolPage  * const parent 是上一个的地址, AutoreleasePoolPage  * child是下一个的地址,当一个AutoreleasePoolPage空间占满后,会再新建一个,连接链表.

想一个对象发送autorelease消息,就是将这个对象到当前AutoreleasePoolPage的栈顶next指针指向的位置

每当调用一次objc_autoreleasePoolPush,runtime想当前AutoreleasePoolPage中加入一个哨兵对象,值为nil,objc_autoreleasePoolPush的返回值是这个哨兵对象的地址,被objc_autoreleasePoolPop作为参数:

1.根据传入的哨兵对象地址找到对应的AutoreleasePoolPage

2.在当前的AutoreleasePoolPage中,将晚于哨兵对象的所有autorelease对象发送一次release消息,并向回移动id *next指针,清理方向也是从id *next开始,跨多个AutoreleasePoolPage,知道哨兵对象.

  ARC下,runtime有一套对autorelease返回值优化的策略.

    编译器代码:  + (instancetype)createSark{

                          id tmp = [self new];

                         return objc_autoreleaseReturnValue(tmp);//遵循谁创建谁释放 objc_autoreleaseReturnValue及时利用一个中转来存储,而没有调用autorelease,避免进行内存管理

}

参考大神文章:http://blog.sunnyxx.com/2014/10/15/behind-autorelease/

猜你喜欢

转载自blog.csdn.net/qq_28551705/article/details/82633105