ViewController创建后释放闪退

问题描述

在做项目时遇到一个闪退问题,查看代码逻辑发现以下代码会造成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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值