From 629ddc76994a27ea59bb6913d0079db8e845cdfe Mon Sep 17 00:00:00 2001 From: Aaron Clarke Date: Mon, 3 Feb 2020 11:30:29 -0800 Subject: [PATCH 1/2] Added a plugin method that gets called when the engine is about to be deleted. --- .../darwin/ios/framework/Headers/FlutterPlugin.h | 13 +++++++++++++ .../darwin/ios/framework/Source/FlutterEngine.mm | 7 +++++++ 2 files changed, 20 insertions(+) diff --git a/shell/platform/darwin/ios/framework/Headers/FlutterPlugin.h b/shell/platform/darwin/ios/framework/Headers/FlutterPlugin.h index 8092217fcfe35..3db7badb3d44d 100644 --- a/shell/platform/darwin/ios/framework/Headers/FlutterPlugin.h +++ b/shell/platform/darwin/ios/framework/Headers/FlutterPlugin.h @@ -223,6 +223,19 @@ typedef void (*FlutterPluginRegistrantCallback)(NSObject* * @param result A callback for submitting the result of the call. */ - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result; +@optional +/** + * Called when a plugin is being removed from a `FlutterEngine`, which is + * usually the result of the `FlutterEngine` being deallocated. This method + * provides the opportunity to do necessary cleanup. + * + * You will only receive this method if you registered your plugin instance with + * the `FlutterEngine` via `-[FlutterPluginRegistry publish:]`. + * + * @param registrar The registrar that was used to publish the plugin. + * + */ +- (void)detachFromEngineForRegistrar:(NSObject*)registrar; @end #pragma mark - diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm index ec22caa532f45..c432c134f4cc3 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm @@ -122,6 +122,13 @@ - (instancetype)initWithName:(NSString*)labelPrefix } - (void)dealloc { + [_pluginPublications enumerateKeysAndObjectsUsingBlock:^(id key, id object, BOOL* stop) { + if ([object respondsToSelector:@selector(detachFromEngineForRegistrar:)]) { + NSObject* registrar = [self registrarForPlugin:key]; + [object detachFromEngineForRegistrar:registrar]; + } + }]; + [_labelPrefix release]; [_pluginPublications release]; _binaryMessenger.parent = nil; From 5684737758b7572737ca3132e22fe5ac39376783 Mon Sep 17 00:00:00 2001 From: Aaron Clarke Date: Mon, 3 Feb 2020 13:54:27 -0800 Subject: [PATCH 2/2] Added unit test. --- .../ios/framework/Source/FlutterEngine.mm | 33 +++++++++++++------ .../ios/framework/Source/FlutterEngineTest.mm | 13 ++++++++ 2 files changed, 36 insertions(+), 10 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm index c432c134f4cc3..55be4043a770e 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm @@ -29,19 +29,21 @@ NSString* const FlutterDefaultDartEntrypoint = nil; +@interface FlutterEngineRegistrar : NSObject +@property(nonatomic, assign) FlutterEngine* flutterEngine; +- (instancetype)initWithPlugin:(NSString*)pluginKey flutterEngine:(FlutterEngine*)flutterEngine; +@end + @interface FlutterEngine () // Maintains a dictionary of plugin names that have registered with the engine. Used by // FlutterEngineRegistrar to implement a FlutterPluginRegistrar. @property(nonatomic, readonly) NSMutableDictionary* pluginPublications; +@property(nonatomic, readonly) NSMutableDictionary* registrars; @property(nonatomic, readwrite, copy) NSString* isolateId; @property(nonatomic, retain) id flutterViewControllerWillDeallocObserver; @end -@interface FlutterEngineRegistrar : NSObject -- (instancetype)initWithPlugin:(NSString*)pluginKey flutterEngine:(FlutterEngine*)flutterEngine; -@end - @implementation FlutterEngine { fml::scoped_nsobject _dartProject; flutter::ThreadHost _threadHost; @@ -98,6 +100,7 @@ - (instancetype)initWithName:(NSString*)labelPrefix _dartProject.reset([project retain]); _pluginPublications = [NSMutableDictionary new]; + _registrars = [[NSMutableDictionary alloc] init]; _platformViewsController.reset(new flutter::FlutterPlatformViewsController()); _binaryMessenger = [[FlutterBinaryMessengerRelay alloc] initWithParent:self]; @@ -122,15 +125,24 @@ - (instancetype)initWithName:(NSString*)labelPrefix } - (void)dealloc { + /// Notify plugins of dealloc. This should happen first in dealloc since the + /// plugins may be talking to things like the binaryMessenger. [_pluginPublications enumerateKeysAndObjectsUsingBlock:^(id key, id object, BOOL* stop) { if ([object respondsToSelector:@selector(detachFromEngineForRegistrar:)]) { - NSObject* registrar = [self registrarForPlugin:key]; + NSObject* registrar = self.registrars[key]; [object detachFromEngineForRegistrar:registrar]; } }]; + /// nil out weak references. + [_registrars + enumerateKeysAndObjectsUsingBlock:^(id key, FlutterEngineRegistrar* registrar, BOOL* stop) { + registrar.flutterEngine = nil; + }]; + [_labelPrefix release]; [_pluginPublications release]; + [_registrars release]; _binaryMessenger.parent = nil; [_binaryMessenger release]; @@ -654,7 +666,10 @@ - (NSString*)lookupKeyForAsset:(NSString*)asset fromPackage:(NSString*)package { - (NSObject*)registrarForPlugin:(NSString*)pluginKey { NSAssert(self.pluginPublications[pluginKey] == nil, @"Duplicate plugin key: %@", pluginKey); self.pluginPublications[pluginKey] = [NSNull null]; - return [[[FlutterEngineRegistrar alloc] initWithPlugin:pluginKey flutterEngine:self] autorelease]; + FlutterEngineRegistrar* result = [[FlutterEngineRegistrar alloc] initWithPlugin:pluginKey + flutterEngine:self]; + self.registrars[pluginKey] = result; + return [result autorelease]; } - (BOOL)hasPlugin:(NSString*)pluginKey { @@ -693,20 +708,18 @@ - (void)setIsGpuDisabled:(BOOL)value { @implementation FlutterEngineRegistrar { NSString* _pluginKey; - FlutterEngine* _flutterEngine; } - (instancetype)initWithPlugin:(NSString*)pluginKey flutterEngine:(FlutterEngine*)flutterEngine { self = [super init]; NSAssert(self, @"Super init cannot be nil"); - _pluginKey = [pluginKey retain]; - _flutterEngine = [flutterEngine retain]; + _pluginKey = [pluginKey copy]; + _flutterEngine = flutterEngine; return self; } - (void)dealloc { [_pluginKey release]; - [_flutterEngine release]; [super dealloc]; } diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEngineTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterEngineTest.mm index a5641da0fb756..4071d26f7e7cf 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterEngineTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterEngineTest.mm @@ -47,4 +47,17 @@ - (void)testSetMessageHandlerBeforeRun { }]); } +- (void)testNotifyPluginOfDealloc { + id plugin = OCMProtocolMock(@protocol(FlutterPlugin)); + OCMStub([plugin detachFromEngineForRegistrar:[OCMArg any]]); + { + id project = OCMClassMock([FlutterDartProject class]); + FlutterEngine* engine = [[FlutterEngine alloc] initWithName:@"engine" project:project]; + NSObject* registrar = [engine registrarForPlugin:@"plugin"]; + [registrar publish:plugin]; + engine = nil; + } + OCMVerify([plugin detachFromEngineForRegistrar:[OCMArg any]]); +} + @end