Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 5075172

Browse files
authored
FlutterViewController notify will dealloc (#12232)
Made the flutter view controllers dealloc notification more generic and started turning off semantics when the view controller is remotely deleted.
1 parent 1a2c16b commit 5075172

File tree

7 files changed

+68
-2
lines changed

7 files changed

+68
-2
lines changed

shell/platform/darwin/ios/framework/Source/FlutterEngine.mm

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ @interface FlutterEngine () <FlutterTextInputDelegate, FlutterBinaryMessenger>
3333
@property(nonatomic, readonly) NSMutableDictionary* pluginPublications;
3434

3535
@property(nonatomic, readwrite, copy) NSString* isolateId;
36+
@property(nonatomic, retain) id<NSObject> flutterViewControllerWillDeallocObserver;
3637
@end
3738

3839
@interface FlutterEngineRegistrar : NSObject <FlutterPluginRegistrar>
@@ -111,6 +112,7 @@ - (void)dealloc {
111112

112113
NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
113114
[center removeObserver:self name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
115+
[_flutterViewControllerWillDeallocObserver release];
114116

115117
[super dealloc];
116118
}
@@ -167,12 +169,21 @@ - (void)setViewController:(FlutterViewController*)viewController {
167169
_viewController = [viewController getWeakPtr];
168170
self.iosPlatformView->SetOwnerViewController(_viewController);
169171
[self maybeSetupPlatformViewChannels];
172+
173+
self.flutterViewControllerWillDeallocObserver =
174+
[[NSNotificationCenter defaultCenter] addObserverForName:FlutterViewControllerWillDealloc
175+
object:viewController
176+
queue:[NSOperationQueue mainQueue]
177+
usingBlock:^(NSNotification* note) {
178+
[self notifyViewControllerDeallocated];
179+
}];
170180
}
171181

172182
- (void)notifyViewControllerDeallocated {
173183
if (!_allowHeadlessExecution) {
174184
[self destroyContext];
175185
}
186+
_viewController.reset();
176187
}
177188

178189
- (void)destroyContext {

shell/platform/darwin/ios/framework/Source/FlutterEngine_Internal.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@
4444
- (FlutterTextInputPlugin*)textInputPlugin;
4545
- (void)launchEngine:(NSString*)entrypoint libraryURI:(NSString*)libraryOrNil;
4646
- (BOOL)createShell:(NSString*)entrypoint libraryURI:(NSString*)libraryOrNil;
47-
- (void)notifyViewControllerDeallocated;
4847

4948
@end
5049

shell/platform/darwin/ios/framework/Source/FlutterViewController.mm

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525

2626
NSNotificationName const FlutterSemanticsUpdateNotification = @"FlutterSemanticsUpdate";
2727

28+
NSNotificationName const FlutterViewControllerWillDealloc = @"FlutterViewControllerWillDealloc";
29+
2830
// This is left a FlutterBinaryMessenger privately for now to give people a chance to notice the
2931
// change. Unfortunately unless you have Werror turned on, incompatible pointers as arguments are
3032
// just a warning.
@@ -521,7 +523,9 @@ - (void)flushOngoingTouches {
521523
}
522524

523525
- (void)dealloc {
524-
[_engine.get() notifyViewControllerDeallocated];
526+
[[NSNotificationCenter defaultCenter] postNotificationName:FlutterViewControllerWillDealloc
527+
object:self
528+
userInfo:nil];
525529
[[NSNotificationCenter defaultCenter] removeObserver:self];
526530
[_ongoingTouches release];
527531
[super dealloc];

shell/platform/darwin/ios/framework/Source/FlutterViewControllerTest.m

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,23 @@
1212
#error ARC must be enabled!
1313
#endif
1414

15+
extern NSNotificationName const FlutterViewControllerWillDealloc;
16+
17+
/// A simple mock class for FlutterEngine.
18+
///
19+
/// OCMockClass can't be used for FlutterEngine sometimes because OCMock retains arguments to
20+
/// invocations and since the init for FlutterViewController calls a method on the
21+
/// FlutterEngine it creates a retain cycle that stops us from testing behaviors related to
22+
/// deleting FlutterViewControllers.
23+
@interface MockEngine : NSObject
24+
@end
25+
26+
@implementation MockEngine
27+
- (void)setViewController:(FlutterViewController*)viewController {
28+
// noop
29+
}
30+
@end
31+
1532
@interface FlutterViewControllerTest : XCTestCase
1633
@end
1734

@@ -244,4 +261,22 @@ - (UITraitCollection*)fakeTraitCollectionWithContrast:(UIAccessibilityContrast)c
244261
return mockTraitCollection;
245262
}
246263

264+
- (void)testWillDeallocNotification {
265+
XCTestExpectation* expectation =
266+
[[XCTestExpectation alloc] initWithDescription:@"notification called"];
267+
id engine = [[MockEngine alloc] init];
268+
FlutterViewController* realVC = [[FlutterViewController alloc] initWithEngine:engine
269+
nibName:nil
270+
bundle:nil];
271+
id observer =
272+
[[NSNotificationCenter defaultCenter] addObserverForName:FlutterViewControllerWillDealloc
273+
object:nil
274+
queue:[NSOperationQueue mainQueue]
275+
usingBlock:^(NSNotification* _Nonnull note) {
276+
[expectation fulfill];
277+
}];
278+
realVC = nil;
279+
[self waitForExpectations:@[ expectation ] timeout:1.0];
280+
}
281+
247282
@end

shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@
1212
#include "flutter/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h"
1313
#include "flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h"
1414

15+
FLUTTER_EXPORT
16+
extern NSNotificationName const FlutterViewControllerWillDealloc;
17+
1518
@interface FlutterViewController ()
1619

1720
- (fml::WeakPtr<FlutterViewController>)getWeakPtr;

shell/platform/darwin/ios/platform_view_ios.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ class PlatformViewIOS final : public PlatformView {
5252
std::unique_ptr<AccessibilityBridge> accessibility_bridge_;
5353
fml::scoped_nsprotocol<FlutterTextInputPlugin*> text_input_plugin_;
5454
fml::closure firstFrameCallback_;
55+
fml::scoped_nsprotocol<NSObject*> dealloc_view_controller_observer_;
5556

5657
// |PlatformView|
5758
void HandlePlatformMessage(fml::RefPtr<flutter::PlatformMessage> message) override;

shell/platform/darwin/ios/platform_view_ios.mm

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,19 @@
4949
accessibility_bridge_.reset();
5050
}
5151
owner_controller_ = owner_controller;
52+
53+
// Add an observer that will clear out the owner_controller_ ivar and
54+
// the accessibility_bridge_ in case the view controller is deleted.
55+
dealloc_view_controller_observer_.reset([[NSNotificationCenter defaultCenter]
56+
addObserverForName:FlutterViewControllerWillDealloc
57+
object:owner_controller_.get()
58+
queue:[NSOperationQueue mainQueue]
59+
usingBlock:^(NSNotification* note) {
60+
// Implicit copy of 'this' is fine.
61+
accessibility_bridge_.reset();
62+
owner_controller_.reset();
63+
}]);
64+
5265
if (owner_controller_) {
5366
ios_surface_ =
5467
[static_cast<FlutterView*>(owner_controller.get().view) createSurface:gl_context_];

0 commit comments

Comments
 (0)