ViewController创建后释放闪退

来源:互联网 发布:c语言 小数比大小 编辑:程序博客网 时间:2024/04/29 11:17

问题描述

在做项目时遇到一个闪退问题,查看代码逻辑发现以下代码会造成crash。

- (IBAction)buttonTouchUpInside:(id)sender {    TestTableViewController *vc = [[TestTableViewController alloc]init];}

是的,你没有看错,上面的代码会造成闪退,TestTableViewController的代码如下:
TestTableViewController.h文件

#import <UIKit/UIKit.h>@interface TestTableViewController : UITableViewController@end

TestTableViewController.m文件

#import "TestTableViewController.h"@interface TestTableViewController ()@end@implementation TestTableViewController- (void)viewDidLoad {    [super viewDidLoad];    __weak TestTableViewController *weakSelf = self;    [self.tableView addObserver:self forKeyPath:@"contentOffset" options:NSKeyValueObservingOptionNew context:nil];}- (void)dealloc {    [self.tableView removeObserver:self forKeyPath:@"contentOffset"];}- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {    NSLog(@"%@",change);}@end

出现闪退的代码是:

__weak TestTableViewController *weakSelf = self;

我看到这里的第一感觉是很意外,因为调用的地方只是init了一个实例,随后该实例作为方法中的临时变量应该就自动释放了,所以不应该会调用到- (void)viewDidLoad方法中的代码。
但是,TestTableViewController作为UITableViewController的子类,当在- (void)dealloc方法中,访问父类的self.tableView时,就触发了- (void)viewDidLoad方法。
随后,在执行__weak TestTableViewController *weakSelf = self;代码时闪退。

在iOS8.4版本的模拟器上错误日志是EXC_BAD_INSTRUCTION(code=EXC_I386_INVOP,subcode=0x0)
而后我发现该问题在iOS10系统中并不会闪退,而只是在运行时输出一个warning问题:

[Warning] Attempting to load the view of a view controller while it is deallocating is not allowed and may result in undefined behavior (<TestTableViewController: 0x7fe85fc01790>)

但是,如果调用的地方是这样的:

- (IBAction)buttonTouchUpInside:(id)sender {    TestTableViewController *vc = [[TestTableViewController alloc]init];    [self presentViewController:vc animated:YES completion:^{        [vc dismissViewControllerAnimated:YES completion:^{        }];    }];}

present显示vc后马上dismiss掉,这样并不会出现闪退问题。
也就是说只要TestTableViewController的viewDidLoad方法执行过,那么在dealloc的时候便不会再触发viewDidLoad方法。

解决方法

该问题发生有三个条件:
1、在dealloc方法中访问了父类的tableView属性。
2、在viewDidLoad方法中定义了weakSelf变量。
3、创建了TestTableViewController的实例变量,但又没有使用。

以上三个条件,前两个都是业务逻辑的要求,一般来说无法避免,所以只能尽可能避免第3个条件的出现,即:ViewController的实例,如果用不到就不要创建。
比如:

- (void)showWithType:(NSInteger)type {    TestTableViewController *vc = [[TestTableViewController alloc]init];    if (type==1) {        vc.title = @"1";    } else if (type==2) {        vc.title = @"2";    } else {        return;    }    [self presentViewController:vc animated:YES completion:nil];}- (void)showWithType2:(NSInteger)type {    NSString *title;    if (type==1) {        title = @"1";    } else if (type==2) {        title = @"2";    } else {        return;    }    TestTableViewController *vc = [[TestTableViewController alloc]init];    vc.title = title;    [self presentViewController:vc animated:YES completion:nil];}

尽量使用showWithType2方法的写法,而不是showWithType,以免出现不可预测的闪退问题。

演示代码

https://code.csdn.net/jhq1990/demo2017-02-18/

1 0