iOS 底层 day29 循环引用 和 内存泄露
2020-10-14 本文已影响0人
望穿秋水小作坊
一、UIView 的 block 写动画
1. 请问下面代码有内存泄露
吗?有循环引用
吗?
// 情况一
@implementation UIViewAnimationsBlock
- (void)viewDidLoad {
[super viewDidLoad];
NSTimeInterval duration = 1000;
[UIView animateWithDuration:duration animations:^{
[self.view.superview layoutIfNeeded];
}];
}
- (void)dealloc {
NSLog(@"%s", __func__);
}
@end
- 没有
内存泄露
,也没有循环引用
。
2. UIView
的 block
写动画时不需要考虑循环引用的原因是?
-
block
会立即执行,所以并不会持有block
。其中duration
延迟时间并不能决定block
执行的时机,block
始终是瞬间执行的。 - 这里涉及了
CoreAnimation
(核心动画)相关的知识:UIView 层
、Layer 层
、data 数据层
-
UIView 层
的block
仅仅是提供了类似快照data
的变化。 - 当真正执行
Animation
动画时才会将"原有状态"
与"执行完 block 的状态"
做一个差值,来去做动画。
二、NSNotificationCenter 的 block 使用
1. 请问下面代码有内存泄露
吗?有循环引用
吗?
// 情况二
@implementation NSNotificationCenterBlock
- (void)viewDidLoad {
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserverForName:@"someNotification"
object:nil
queue:[NSOperationQueue mainQueue]
usingBlock:^(NSNotification * notification) {
NSLog(@"%@", self);
}];
}
- (void)remove {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)dealloc {
[self remove];
NSLog(@"%s", __func__);
}
@end
- 有
内存泄露
,没有循环引用
。
2. 情况二解析
-
[NSNotificationCenter defaultCenter]
是一个单例,它需要在接到通知时候调用block
,所以[NSNotificationCenter defaultCenter]
需要持有block
,会对block
调用-copy
方法。 -
[NSNotificationCenter defaultCenter]
持有block
,block
持有self
;[NSNotificationCenter defaultCenter]
不会释放,因此self
也不会得到释放,也不会调用-dealloc
方法。 - 所以不存在
循环引用
,但是存在内存泄露
。
3. 请问下面代码有内存泄露
吗?有循环引用
吗?
// 情况三
@interface NSNotificationCenterIVARBlock ()
@property (nonatomic, strong) id observer;
@end
@implementation NSNotificationCenterIVARBlock
- (void)viewDidLoad {
[super viewDidLoad];
self.observer = [[NSNotificationCenter defaultCenter] addObserverForName:@"testKey"
object:nil
queue:nil
usingBlock:^(NSNotification *note) {
NSLog(@"%@", self);
}];
}
- (void)remove {
[[NSNotificationCenter defaultCenter] removeObserver:self.observer];
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[self remove];
}
- (void)dealloc {
NSLog(@"%s", __func__);
}
@end
- 存在
循环引用
,也存在内存泄露
。
4. 情况三解析
-
self
持有observer
,observer
持有block
,block
指持有self
;这就造成了循环引用。 - 虽然
-remove方法调用
让observer
从[NSNotificationCenter defaultCenter]
移除,但是不能改变已经形成的循环引用。
5. 如何利用 Xcode 检查情况三存在的内存泄露和循环引用?
- 方法一:可用 Xcode-instruments-Leak 工具查看
- 打开方法
Product → profile → leaks
- 方法二:借助 Xcode 的
Debug Memory Graph
功能查看
Xcode 的 `Debug Memory Graph`开启
三、GCD 和 NSOperationQueue 的 block 使用
1. 请问下面代码有内存泄露
吗?有循环引用
吗?
// 情况四
@interface GCDBlock ()
@property(nonatomic, strong) dispatch_queue_t myQueue;
@end
@implementation GCDBlock
- (void)viewDidLoad {
[super viewDidLoad];
self.myQueue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_SERIAL);
dispatch_async(self.myQueue, ^{
[self doSomething];
});
}
- (void)doSomething {
NSLog(@"%s", __func__);
}
- (void)dealloc {
NSLog(@"%s", __func__);
}
@end
- 没有
内存泄露
,也没有循环引用
。
2. 请问下面代码有内存泄露
吗?有循环引用
吗?
// 情况五
@implementation NSOperationQueueBlock
- (void)viewDidLoad {
[super viewDidLoad];
[[NSOperationQueue mainQueue] addOperationWithBlock:^{ NSLog(@"%@",self); }];
}
- (void)dealloc {
NSLog(@"%s", __func__);
}
@end
- 没有
内存泄露
,也没有循环引用
。
3. 情况四 情况五解析
- 针对
GCD
和NSOperationQueue
的问题,我们只需记住,block 执行完毕之后,block 将会被释放掉。从而破除了循环引用和内存泄露
。