Description
前面有好几个人已经提到了:
[[NSNotificationCenter defaultCenter] addObserverForName:@"someNotification"
object:nil
queue:[NSOperationQueue mainQueue]
usingBlock:^(NSNotification * notification) {
self.someProperty = xyz; }];
存在内存泄漏;
而下面的代码并不存在内存泄漏:
dispatch_group_async(self.operationGroup, self.serialQueue, ^{
[self doSomething];
});
个人理解:
首先下面这段话是没有问题的
“所谓“引用循环”是指双向的强引用,所以那些“单向的强引用”(block 强引用 self )没有问题”
那么为什么第一段NSNotificationCenter的代码会有内存泄漏问题呢?其实和循环引用没有关系;这里block强引用了self, 但是self并没有强引用block; 所以没有循环引用。
这里出现内存泄漏问题实际上是因为,[NSNoficationCenter defaultCenter]
持有了block, 这个block持有了self; 而[NSNoficationCenter defaultCenter]
是一个单例,因此这个单例持有了self, 从而导致self不被释放。以下来自API文档:
The block is copied by the notification center and (the copy) held until the observer registration is removed.
但整个过程中并没有循环引用,因为self没有持有NotificationCenter, 也没有持有block。即使self持有这个Observer, 并没有任何证据或者文档标明Observer会持有这个block, 所以作者的解释是不正确的。而我个人的理解,这里Observer应该是不持有block的,因为只需要NSNotificationCenter同时持有Observer和block即可实现API所提供的功能, 这里也不存在循环引用。
针对第二段GCD的问题,实际上,self确实持有了queue; 而block也确实持有了self; 但是并没有证据或者文档表明这个queue一定会持有block; 而且即使queue持有了block, 在block执行完毕的时候,由于需要将任务从队列中移除,因此完全可以解除queue对block的持有关系,所以实际上这里也不存在循环引用。下面的测试代码可以验证这一点(其中HTMemoryDetecter
有一个属性name):
HTMemoryDetecter *detector = [[HTMemoryDetecter alloc] init];
dispatch_group_async(self.operationGroup, self.serialQueue, ^{
NSLog(@"dispatch_async demoGCDRetainCycle");
[self.testList addObject:@"demoGCDRetainCycle2"];
detector.name = @"测试";
NSLog(@"Detecor 's name: %@", detector.name);
});
那么会看到先打印出dispatch_async demoGCDRetainCycle
, 然后打印出这个detecotr
的name, 然后执行HTMemoryDetecter
的dealloc方法。也就是说在这个block执行完毕的时候,仅由这个block持有的detector就会被释放了, 从而验证这个block都被释放了,即使对应的queue还存在。
什么时候这里会有循环引用呢?仍然是当self持有block的时候,例如这个block是self的一个strong的属性,但这就和GCD的调用无关了,这个时候无论是否调用GCD的API都会有循环引用的。
Activity
liftliftlift commentedon Apr 14, 2017
Good �correction!
ChenYilong commentedon Jun 1, 2020
感谢指正, 我修改下答案
Reference:
#61
#72
ChenYilong commentedon Jun 1, 2020
见
https://github.com/ChenYilong/iOSInterviewQuestions/blob/master/01%E3%80%8A%E6%8B%9B%E8%81%98%E4%B8%80%E4%B8%AA%E9%9D%A0%E8%B0%B1%E7%9A%84iOS%E3%80%8B%E9%9D%A2%E8%AF%95%E9%A2%98%E5%8F%82%E8%80%83%E7%AD%94%E6%A1%88/%E3%80%8A%E6%8B%9B%E8%81%98%E4%B8%80%E4%B8%AA%E9%9D%A0%E8%B0%B1%E7%9A%84iOS%E3%80%8B%E9%9D%A2%E8%AF%95%E9%A2%98%E5%8F%82%E8%80%83%E7%AD%94%E6%A1%88%EF%BC%88%E4%B8%8B%EF%BC%89.md#39-%E4%BD%BF%E7%94%A8%E7%B3%BB%E7%BB%9F%E7%9A%84%E6%9F%90%E4%BA%9Bblock-api%E5%A6%82uiview%E7%9A%84block%E7%89%88%E6%9C%AC%E5%86%99%E5%8A%A8%E7%94%BB%E6%97%B6%E6%98%AF%E5%90%A6%E4%B9%9F%E8%80%83%E8%99%91%E5%BC%95%E7%94%A8%E5%BE%AA%E7%8E%AF%E9%97%AE%E9%A2%98
fix: 🐛 fix NO.39 question error