問題の説明
プロジェクトで作業しているときに、クラッシュの問題が発生しました。コードロジックを見ると、次のコードがクラッシュを引き起こすことがわかりました。
- (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;
ここで最初に目にするのは非常に予想外のことです。呼び出し場所はインスタンスの初期化のみであり、メソッドの一時変数としてのインスタンスは自動的に解放されるため- (void)viewDidLoad
、メソッドのコードは呼び出されないはずです。
ただし、テストテーブルビューコントローラーは- (void)dealloc
、UITableViewControllerのサブクラスとして、メソッドで親クラスのself.tableViewにアクセスしたときに- (void)viewDidLoad
メソッドをトリガーします。
その後、__weak TestTableViewController *weakSelf = self;
コードの実行時にクラッシュしました。
エラーログは、シミュレータのiOS8.4バージョンにありEXC_BAD_INSTRUCTION(code=EXC_I386_INVOP,subcode=0x0)
ます。
次に、iOS10システムでは問題がフラッシュバックしないことがわかりましたが、実行時に警告の問題のみが出力されます。
[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:^{
}];
}];
}
現在がvcを示した直後に却下するので、フラッシュバックの問題は発生しません。
つまり、TestTableViewControllerのviewDidLoadメソッドが実行されている限り、dealloc中にviewDidLoadメソッドはトリガーされません。
解決
この問題には3つの条件があり
ます。1。dealloc
親クラスのtableViewプロパティがメソッドでアクセスされます。2.weakSelf変数がメソッドで定義さ
れていviewDidLoad
ます。
3. TestTableViewControllerのインスタンス変数が作成されますが、使用されません。
上記の3つの条件のうち、最初の2つはビジネスロジックの要件です。一般的には回避できません。したがって、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];
}
予期しないクラッシュの問題を回避するために、showWithTypeの代わりにshowWithType2メソッドを使用してみてください。