一、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
leaks 查看NSNotificationCenterIVARBlock 内存泄露图
- 方法二:借助 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 将会被释放掉。从而破除了循环引用和内存泄露
。
网友评论