From 94fa5f4edf3b822cdc8a94a4dc87a28274642830 Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Mon, 10 Apr 2023 20:45:27 -0700 Subject: [PATCH] [ios] fix background crash when FlutterViewController is destroyed after engine destroyed context (flutter/engine#41037) [ios] fix background crash when FlutterViewController is destroyed after engine destroyed context --- .../ios/framework/Source/FlutterEngine.mm | 2 +- .../ScenariosTests/FlutterEngineTest.m | 38 +++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm b/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm index 422a6155e40..f434b9305bc 100644 --- a/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm +++ b/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm @@ -434,7 +434,7 @@ static constexpr int kNumProfilerSamplesPerSec = 5; _undoManagerPlugin.get().viewController = nil; if (!_allowHeadlessExecution) { [self destroyContext]; - } else { + } else if (_shell) { flutter::PlatformViewIOS* platform_view = [self iosPlatformView]; if (platform_view) { platform_view->SetOwnerViewController({}); diff --git a/engine/src/flutter/testing/scenario_app/ios/Scenarios/ScenariosTests/FlutterEngineTest.m b/engine/src/flutter/testing/scenario_app/ios/Scenarios/ScenariosTests/FlutterEngineTest.m index 3c9119af617..218bc780a9b 100644 --- a/engine/src/flutter/testing/scenario_app/ios/Scenarios/ScenariosTests/FlutterEngineTest.m +++ b/engine/src/flutter/testing/scenario_app/ios/Scenarios/ScenariosTests/FlutterEngineTest.m @@ -12,6 +12,8 @@ @implementation FlutterEngineTest +extern NSNotificationName const FlutterViewControllerWillDealloc; + - (void)testIsolateId { FlutterEngine* engine = [[FlutterEngine alloc] initWithName:@"test" project:nil]; XCTAssertNil(engine.isolateId); @@ -48,4 +50,40 @@ XCTAssertNil(engine.lifecycleChannel); } +// https://github.com/flutter/flutter/issues/123776 +- (void)testReleaseViewControllerAfterDestroyContextInHeadlessMode { + FlutterEngine* engine = [[FlutterEngine alloc] initWithName:@"test" + project:nil + allowHeadlessExecution:YES]; + XCTAssertNil(engine.navigationChannel); + XCTAssertNil(engine.platformChannel); + XCTAssertNil(engine.lifecycleChannel); + + XCTAssertTrue([engine run]); + + XCTAssertNotNil(engine.navigationChannel); + XCTAssertNotNil(engine.platformChannel); + XCTAssertNotNil(engine.lifecycleChannel); + XCTestExpectation* expectation = + [[XCTestExpectation alloc] initWithDescription:@"notification called"]; + @autoreleasepool { + FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:engine + nibName:nil + bundle:nil]; + [engine setViewController:viewController]; + [engine destroyContext]; + [[NSNotificationCenter defaultCenter] addObserverForName:FlutterViewControllerWillDealloc + object:nil + queue:[NSOperationQueue mainQueue] + usingBlock:^(NSNotification* _Nonnull note) { + [expectation fulfill]; + }]; + viewController = nil; + } + [self waitForExpectations:@[ expectation ] timeout:30.0]; + XCTAssertNil(engine.navigationChannel); + XCTAssertNil(engine.platformChannel); + XCTAssertNil(engine.lifecycleChannel); +} + @end