这一次,我们创建一个最简单的视图控制器不被释放的场景,通过断点调试的方法,来简单梳理一下。
准备
新建一个视图控制器AAViewController
添加block.不使用__weak
引用,这样就会构造循环引用的最基本的场景。
@interface AAViewController ()
@property (nonatomic, copy) dispatch_block_t block;
@end
@implementation AAViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.block = ^(){
NSLog(@"%@", self);
};
}
@end
将它入导航控制器的栈。然后点击返回按钮,退出该视图控制器,会发现出现一个提示循环引用的弹框。
![](https://img.haomeiwen.com/i8350700/4882b934744d8d3b.png)
调试
全局搜索UIAlertView,找到该库中只有一个地方调用了UIAlertView
.是在MLeaksMessenger
的一个类方法
+ (void)alertWithTitle:(NSString *)title
message:(NSString *)message
delegate:(id<UIAlertViewDelegate>)delegate
additionalButtonTitle:(NSString *)additionalButtonTitle {
[alertView dismissWithClickedButtonIndex:0 animated:NO];
UIAlertView *alertViewTemp = [[UIAlertView alloc] initWithTitle:title
message:message
delegate:delegate
cancelButtonTitle:@"OK"
otherButtonTitles:additionalButtonTitle, nil];
[alertViewTemp show];
alertView = alertViewTemp;
NSLog(@"%@: %@", title, message);
}
在这里打一个断点。可以看到整个调用栈。
![](https://img.haomeiwen.com/i8350700/2f373db1eef53283.png)
定位到了调用方法的源头是 willDealloc
。搜索看到UIViewController
的category
中有该方法。显然它这就是未被释放控制器AAViewController
调用的方法。
- (void)swizzled_viewDidDisappear:(BOOL)animated {
[self swizzled_viewDidDisappear:animated];
if ([objc_getAssociatedObject(self, kHasBeenPoppedKey) boolValue]) {
[self willDealloc];
}
}
其中swizzled_viewDidDisappear
是和viewDidDisappear
交换的方法。那么说明这个方法即是视图控制器调用viewDidDisappear
的时机执行。调用要满足[objc_getAssociatedObject(self, kHasBeenPoppedKey) boolValue]
为true.全局搜索kHasBeenPoppedKey
,发现是在UINavigationController+MemoryLeak
的- (UIViewController *)swizzled_popViewControllerAnimated:(BOOL)animated
中设置为@(YES)
.它也是导航控制器popViewControllerAnimated:
的交换方法,也就是说每次导航控制器pop AAViewController的时候,就将kHasBeenPoppedKey
设置为YES。因为AAViewController
从导航控制器Pop后,认为视图控制器应该被释放。
网友评论