有关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/