前言
内存管理作为iOS最基础的存在,其重要性不言而喻.平时因为忙业务,遇到一些内存相关的问题,然后才有了这篇小结.
AutoreleasePool
几个Tips:
- AutoreleasePool在非手动添加的情况下,是在当前runloop完了之后才会释放对象.
- 子线程的runloop默认不开启,那系统会自动添加AutoreleasePool吗?答案是肯定的,从这篇文章可以看到原理,通过调用
autoreleaseNoPage
方法来进行处理.- 以new,alloc初始化的对象编译器会自动为这个对象添加release语句,而类似于
id object1 = object2;
这种会自动包裹一个AutoreleasePool.- 当我们在使用
enumerateObjectsUsingBlock
这个方法进行遍历的时候,内部也会自动包裹一个AutoreleasePool,所以不需要手动添加.
循环引用
这里先给一段代码,两个类TestA,TestB,重写了dealloc
方法,便于观察释放与否.TestA内有一个TestB类型的属性b
,TestB内有一个TestA类型的属性a
.
TestA *ta = [[TestA alloc] init];
TestB *tb = [[TestB alloc] init];
ta.b = tb;//A的b属性指向tb
tb.a = ta;//B的a属性是想ta
这里毫无疑问会有循环引用,于是自然会想到:
ta = nil;
或者
tb = nil;
但是结果运行之后依然没有打印dealloc
方法.这是为什么呢?
用Leaks分析循环引用关系:
我们发现循环的链条关键点在于
_a
和_b
,我们应该打断这两条蓝线其中的一条.
ta.b = nil;
或则
tb.a = nil;
然后运行发现对象确实被销毁了.
这里令我百思不得其解的是:ta或者tb都是这个链条的一部分,为什么对他们置为nil不产生效果呢?
我尝试这样写:
ta.b = tb;//A的b属性指向tb
ta = nil;
tb.a = ta;//B的a属性是想ta
发现这样也是可以销毁的,道理很简单:循环引用还没建立就销毁一个对象.这样感觉也没有意义.之后和小伙伴们讨论了一下,得出如下值得验证的结论:
当我设置
ta = nil 或者 tb = nil
的时候,其实并没有打断这个循环引用的链条,他们的循环引用已经形成了,ta
持有b
,b
的指针指向tb
,如果我们要打破这个循环引用首先应该切断指向ta
或者tb
的指针,也就是ta.b
或者tb.a
,是这两个指针直接指向的两个对象,切断这种指向才是必要之举.
其实还有些许迷惑,希望有人能给出一些指点.
参考资料
http://blog.sunnyxx.com/2014/10/15/behind-autorelease/
https://www.jianshu.com/p/f87f40592023
网友评论