@@ -626,14 +626,16 @@ ARC相对于MRC,不是在编译时添加retain/release/autorelease这么简单
626626 [ 《第33题,答案可能不是很准确 #15 》] ( https://github.com/ChenYilong/iOSInterviewQuestions/issues/15 )
627627
628628### 34. 不手动指定autoreleasepool的前提下,一个autorealese对象在什么时刻释放?(比如在一个vc的viewDidLoad中创建)
629+
630+
629631分两种情况:手动干预释放时机、系统自动去释放。
630632
631633
632634 1 . 手动干预释放时机--指定 ` autoreleasepool `
633635 就是所谓的:当前作用域大括号结束时释放。
634636 2 . 系统自动去释放--不手动指定 ` autoreleasepool `
635637
636- ` __autoreleasing ` 修饰的 ` autorelease ` 对象,是在创建好之后调用` objc_autorelease ` 会被添加到最近一次创建的自动释放池中,并会在当前的 runloop 迭代结束时执行pop函数时释放 。
638+ ` __autoreleasing ` 修饰的 ` autorelease ` 对象,是在创建好之后调用` objc_autorelease ` 会被添加到最近一次创建的自动释放池中,并且autorelease对象什么时候调用release,是由RunLoop来控制的:会在当前的 runloop 休眠之前,执行pop函数、调用 release 时释放 。
637639
638640释放的时机总结起来,可以用下图来表示:
639641
@@ -648,26 +650,27 @@ ARC相对于MRC,不是在编译时添加retain/release/autorelease这么简单
648650
649651我们都知道:
650652
651- ** 所有 autorelease 的对象,在出了作用域之后,会被自动添加到最近创建的自动释放池中。 **
653+ ` __autoreleasing ` 修饰的 ` autorelease ` 对象,是在创建好之后调用 ` objc_autorelease ` 加入到释放池。
652654
653655但是如果每次都放进应用程序的 ` main.m ` 中的 autoreleasepool 中,迟早有被撑满的一刻。这个过程中必定有一个释放的动作。何时?
654656
655- 在一次完整的运行循环结束之前 ,会被销毁。
657+ 在一次完整的 RunLoop 休眠之前 ,会被销毁。
656658
657- 那什么时间会创建自动释放池?运行循环检测到事件并启动后 ,就会创建自动释放池。
659+ 那什么时间会创建自动释放池? RunLoop 检测到事件并启动后 ,就会创建自动释放池。
658660
659661~~ “子线程的 runloop 默认是不工作,无法主动创建,必须手动创建。”(表述不准确, 见 issue #82 #https://github.com/ChenYilong/iOSInterviewQuestions/issues/82) ~~
660662
661- 从 ` RunLoop ` 源代码中可知,子线程默认是没有 ` RunLoop ` 的,如果需要在子线程开启 ` RunLoop ` ,则需要调用 ` [NSRunLoop CurrentRunLoop] ` 方法,它内部实现是先检查线程,如果发现是子线程,以懒加载的形式 创建一个子线程的 ` RunLoop ` 。并存储在一个全局的 可变字典里。编程人员在调用 ` [NSRunLoop CurrentRunLoop] ` 时,是自动创建 ` RunLoop ` 的,而没法手动创建。
663+ 从 ` RunLoop ` 源代码中可知,子线程默认是没有 ` RunLoop ` 的,如果需要在子线程开启 ` RunLoop ` ,则需要调用 ` [NSRunLoop CurrentRunLoop] ` 方法,它内部实现是先检查线程,如果发现是子线程,以懒加载的形式 创建一个子线程的 ` RunLoop ` 。并存储在一个全局的 可变字典里。开发者在调用 ` [NSRunLoop CurrentRunLoop] ` 时,是系统自动创建 ` RunLoop ` 的,而没法手动创建。
662664
663665自定义的 NSOperation 和 NSThread 需要手动创建自动释放池。比如: 自定义的 NSOperation 类中的 main 方法里就必须添加自动释放池。否则出了作用域后,自动释放对象会因为没有自动释放池去处理它,而造成内存泄露。
664666
665- 但对于 blockOperation 和 invocationOperation 这种默认的Operation ,系统已经帮我们封装好了,不需要手动创建自动释放池。
666-
667+ 但对于 blockOperation 和 invocationOperation 这种默认的 Operation ,系统已经帮我们封装好了,不需要手动创建自动释放池。
667668
668669@autoreleasepool 当自动释放池被销毁或者耗尽时,会向自动释放池中的所有对象发送 release 消息,释放自动释放池中的所有对象。
669670
670- 如果在一个vc的viewDidLoad中创建一个 Autorelease对象,那么该对象会在 viewDidAppear 方法执行前就被销毁了。
671+ 举一个例子: 如果在一个vc的viewDidLoad中创建一个 Autorelease对象,那么该对象会在 viewDidAppear 方法执行前就被销毁了。
672+
673+ 注意: 本次论述, 并不适用于 TaggedPointer 类型.
671674
672675参考链接:[ 《黑幕背后的Autorelease》] ( http://blog.sunnyxx.com/2014/10/15/behind-autorelease/ )
673676
@@ -1437,12 +1440,13 @@ int main(int argc, char * argv[]) {
14371440
14381441情况二这里出现内存泄漏问题实际上是因为:
14391442
1440- - ` [NSNoficationCenter defaultCenter] ` 持有了 ` block ` ,
1443+ - ` [NSNoficationCenter defaultCenter] ` 持有了 ` block `
14411444 - 这个 ` block ` 持有了 ` self ` ;
14421445 - 而 ` [NSNoficationCenter defaultCenter] ` 是一个单例,因此这个单例持有了 ` self ` , 从而导致 ` self ` 不被释放。
14431446
14441447![ https://github.com/ChenYilong ] ( https://tva1.sinaimg.cn/large/007S8ZIlgy1gfcrlp0gn0j30z40lwag6.jpg )
14451448
1449+ 这个结论可参考参考issue中讨论:[ 《第39题的一些疑问 #138 》] ( https://github.com/ChenYilong/iOSInterviewQuestions/issues/138 )
14461450
14471451
14481452
@@ -1475,11 +1479,21 @@ addObserverForName:object:queue:usingBlock:]( https://developer.apple.com/docume
14751479[[NSOperationQueue mainQueue ] addOperationWithBlock: ^{ self.someProperty = xyz; }] ;
14761480 ```
14771481
1478- 这个因为 `[NSOperationQueue mainQueue]` 并非单例,所以并没有内存泄漏。
1482+ <p><del> 这个因为 `[NSOperationQueue mainQueue]` 并非单例,所以并没有内存泄漏。
14791483 见下图:
14801484
1481- 
1485+ https://tva1.sinaimg.cn/large/007S8ZIlgy1gfct4s2979j30y00lq0y0.jpg
1486+ (此图有问题, 请忽略, 请参考下文的正确描述)
14821487
1488+ </del></p>
1489+
1490+ 在 Gnustep 源码中可以证实
1491+ `[NSOperationQueue mainQueue]` 是单例,然后参考 `addOperationWithBlock` 源码可知:
1492+
1493+ 虽然是单例,但它并不持有 `block`,不会造成循环引用,传递完成后就销毁了,不会造成无法释放的内存泄漏问题。
1494+
1495+ 参考issue中讨论:[《第39题的一些疑问 #138》](https://github.com/ChenYilong/iOSInterviewQuestions/issues/138)
1496+
14831497-------------
14841498
14851499针对情况四 `GCD` 的问题,实际上,self确实持有了queue; 而block也确实持有了self; 但是并没有证据或者文档表明这个queue一定会持有block; 而且即使queue持有了block, 在block执行完毕的时候,由于需要将任务从队列中移除,因此完全可以解除queue对block的持有关系,所以实际上这里也不存在循环引用。下面的测试代码可以验证这一点(其中`CYLUser`有一个属性name):
0 commit comments