From 044ed265107809698fcf627a46f158675223e1ab Mon Sep 17 00:00:00 2001 From: Dan Field Date: Fri, 16 Nov 2018 10:17:25 -0800 Subject: [PATCH 01/13] Break cycle between FlutterViewController and FlutterEngine --- .../ios/framework/Source/FlutterViewController.mm | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm index 80e03e28bd8bf..177508c7e0c74 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm @@ -37,6 +37,7 @@ @implementation FlutterViewController { BOOL _initialized; BOOL _viewOpaque; BOOL _engineNeedsLaunch; + BOOL _engineIsOwnedByMe; } #pragma mark - Manage and override all designated initializers @@ -50,6 +51,7 @@ - (instancetype)initWithEngine:(FlutterEngine*)engine _viewOpaque = YES; _engine.reset([engine retain]); _engineNeedsLaunch = NO; + _engineIsOwnedByMe = NO; _flutterView.reset([[FlutterView alloc] initWithDelegate:_engine opaque:self.isViewOpaque]); _weakFactory = std::make_unique>(self); @@ -71,6 +73,7 @@ - (instancetype)initWithProject:(FlutterDartProject*)projectOrNil _flutterView.reset([[FlutterView alloc] initWithDelegate:_engine opaque:self.isViewOpaque]); [_engine.get() createShell:nil libraryURI:nil]; _engineNeedsLaunch = YES; + _engineIsOwnedByMe = YES; [self performCommonViewControllerInitialization]; } @@ -387,9 +390,9 @@ - (void)viewWillAppear:(BOOL)animated { if (_engineNeedsLaunch) { [_engine.get() launchEngine:nil libraryURI:nil]; - [_engine.get() setViewController:self]; _engineNeedsLaunch = NO; } + [_engine.get() setViewController:self]; // Only recreate surface on subsequent appearances when viewport metrics are known. // First time surface creation is done on viewDidLayoutSubviews. @@ -421,12 +424,15 @@ - (void)viewDidDisappear:(BOOL)animated { TRACE_EVENT0("flutter", "viewDidDisappear"); [self surfaceUpdated:NO]; [[_engine.get() lifecycleChannel] sendMessage:@"AppLifecycleState.paused"]; - + [_engine.get() setViewController:nil]; [super viewDidDisappear:animated]; } - (void)dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self]; + if (_engineIsOwnedByMe) { + [_engine.release() dealloc]; + } [super dealloc]; } From b74b979cbdda5c7d5fc37302ce1c03e7b86cef8e Mon Sep 17 00:00:00 2001 From: Dan Field Date: Fri, 16 Nov 2018 11:39:21 -0800 Subject: [PATCH 02/13] shutdown instead of dealloc --- .../darwin/ios/framework/Headers/FlutterEngine.h | 5 +++++ .../darwin/ios/framework/Source/FlutterEngine.mm | 9 +++++++++ .../darwin/ios/framework/Source/FlutterViewController.mm | 2 +- 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/shell/platform/darwin/ios/framework/Headers/FlutterEngine.h b/shell/platform/darwin/ios/framework/Headers/FlutterEngine.h index 1d435acd80b7f..bf22f78a05ae7 100644 --- a/shell/platform/darwin/ios/framework/Headers/FlutterEngine.h +++ b/shell/platform/darwin/ios/framework/Headers/FlutterEngine.h @@ -97,6 +97,11 @@ FLUTTER_EXPORT */ - (BOOL)runWithEntrypoint:(NSString*)entrypoint libraryURI:(NSString*)uri; +/** + * Shuts down the engine if it's running. + */ +- (void)shutdown; + /** * Sets the `FlutterViewController` for this instance. The FlutterEngine must be * running (e.g. a successful call to `-runWithEntrypoint:` or `-runWithEntrypoint:libraryURI`) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm index c5f3b8d9accf0..31345c6b2c899 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm @@ -354,6 +354,15 @@ - (BOOL)createShell:(NSString*)entrypoint libraryURI:(NSString*)libraryURI { return _shell != nullptr; } +- (void)shutdown { + if (_shell == nullptr) { + return; + } + [self setViewController:nil]; + _shell.reset(); + _threadHost.Reset(); +} + - (BOOL)runWithEntrypoint:(NSString*)entrypoint libraryURI:(NSString*)libraryURI { if ([self createShell:entrypoint libraryURI:libraryURI]) { [self launchEngine:entrypoint libraryURI:libraryURI]; diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm index 177508c7e0c74..03fb578440220 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm @@ -431,7 +431,7 @@ - (void)viewDidDisappear:(BOOL)animated { - (void)dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self]; if (_engineIsOwnedByMe) { - [_engine.release() dealloc]; + [_engine.get() shutdown]; } [super dealloc]; } From aecd39fb3a3ac132f4b148621baa3f48a22595d1 Mon Sep 17 00:00:00 2001 From: Dan Field Date: Fri, 16 Nov 2018 12:06:46 -0800 Subject: [PATCH 03/13] cleanup more resources on shutdown --- .../ios/framework/Source/FlutterEngine.mm | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm index 31345c6b2c899..bae509d4499ba 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm @@ -76,7 +76,6 @@ - (instancetype)initWithName:(NSString*)labelPrefix project:(FlutterDartProject* _dartProject.reset([projectOrNil retain]); _pluginPublications = [NSMutableDictionary new]; - _publisher.reset([[FlutterObservatoryPublisher alloc] init]); _platformViewsController.reset(new shell::FlutterPlatformViewsController()); [self setupChannels]; @@ -221,8 +220,6 @@ - (void)setupChannels { _textInputPlugin.get().textInputDelegate = self; _platformPlugin.reset([[FlutterPlatformPlugin alloc] initWithEngine:[self getWeakPtr]]); - - [self maybeSetupPlatformViewChannels]; } - (void)maybeSetupPlatformViewChannels { @@ -348,6 +345,11 @@ - (BOOL)createShell:(NSString*)entrypoint libraryURI:(NSString*)libraryURI { FML_LOG(ERROR) << "Could not start a shell FlutterEngine with entrypoint: " << entrypoint.UTF8String; } else { + [self setupChannels]; + if (!_platformViewsController) { + _platformViewsController.reset(new shell::FlutterPlatformViewsController()); + } + _publisher.reset([[FlutterObservatoryPublisher alloc] init]); [self maybeSetupPlatformViewChannels]; } @@ -359,6 +361,22 @@ - (void)shutdown { return; } [self setViewController:nil]; + + _publisher.reset(); + + _platformViewsController.reset(); + + _platformPlugin.reset(); + _textInputPlugin.reset(); + _localizationChannel.reset(); + _navigationChannel.reset(); + _platformChannel.reset(); + _platformViewsChannel.reset(); + _textInputChannel.reset(); + _lifecycleChannel.reset(); + _systemChannel.reset(); + _settingsChannel.reset(); + _shell.reset(); _threadHost.Reset(); } From c106c3700abbc0bec0fa44fe02aeefca47d8d89c Mon Sep 17 00:00:00 2001 From: Dan Field Date: Fri, 16 Nov 2018 16:01:16 -0800 Subject: [PATCH 04/13] Semantics leaks --- .../framework/Source/accessibility_bridge.mm | 51 +++++++++---------- 1 file changed, 25 insertions(+), 26 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm b/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm index d744e5fc06605..47091389c1829 100644 --- a/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm +++ b/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm @@ -82,13 +82,15 @@ @implementation FlutterCustomAccessibilityAction { */ @interface SemanticsObjectContainer : NSObject - (instancetype)init __attribute__((unavailable("Use initWithSemanticsObject instead"))); -- (instancetype)initWithSemanticsObject:(SemanticsObject*)semanticsObject +- (instancetype)initWithSemanticsObject:(fml::WeakPtr)semanticsObject bridge:(fml::WeakPtr)bridge NS_DESIGNATED_INITIALIZER; @end @implementation SemanticsObject { - SemanticsObjectContainer* _container; + fml::scoped_nsobject _container; + std::unique_ptr> _weakFactory; + } #pragma mark - Override base class designated initializers @@ -111,6 +113,7 @@ - (instancetype)initWithBridge:(fml::WeakPtr)bridge _bridge = bridge; _uid = uid; self.children = [[[NSMutableArray alloc] init] autorelease]; + _weakFactory = std::make_unique>(self); } return self; @@ -123,8 +126,6 @@ - (void)dealloc { [_children removeAllObjects]; [_children dealloc]; _parent = nil; - [_container release]; - _container = nil; [super dealloc]; } @@ -268,9 +269,9 @@ - (CGRect)globalRect { - (id)accessibilityContainer { if ([self hasChildren] || [self uid] == kRootNodeId) { if (_container == nil) - _container = [[SemanticsObjectContainer alloc] initWithSemanticsObject:self - bridge:[self bridge]]; - return _container; + _container.reset([[SemanticsObjectContainer alloc] initWithSemanticsObject:_weakFactory->GetWeakPtr() + bridge:[self bridge]]); + return _container.get(); } if ([self parent] == nil) { // This can happen when we have released the accessibility tree but iOS is @@ -395,7 +396,7 @@ - (UIAccessibilityTraits)accessibilityTraits { @end @implementation SemanticsObjectContainer { - SemanticsObject* _semanticsObject; + fml::WeakPtr _semanticsObject; fml::WeakPtr _bridge; } @@ -408,47 +409,40 @@ - (instancetype)init { return nil; } -- (instancetype)initWithSemanticsObject:(SemanticsObject*)semanticsObject +- (instancetype)initWithSemanticsObject:(fml::WeakPtr)semanticsObject bridge:(fml::WeakPtr)bridge { - FML_DCHECK(semanticsObject != nil) << "semanticsObject must be set"; + FML_DCHECK(semanticsObject.get() != nil) << "semanticsObject must be set"; self = [super init]; if (self) { _semanticsObject = semanticsObject; - // The pointer is managed manually. - [_semanticsObject retain]; _bridge = bridge; } return self; } -- (void)dealloc { - [_semanticsObject release]; - [super dealloc]; -} - #pragma mark - UIAccessibilityContainer overrides - (NSInteger)accessibilityElementCount { - return [[_semanticsObject children] count] + 1; + return [[_semanticsObject.get() children] count] + 1; } - (nullable id)accessibilityElementAtIndex:(NSInteger)index { if (index < 0 || index >= [self accessibilityElementCount]) return nil; if (index == 0) - return _semanticsObject; - SemanticsObject* child = [_semanticsObject children][index - 1]; + return _semanticsObject.get(); + SemanticsObject* child = [_semanticsObject.get() children][index - 1]; if ([child hasChildren]) return [child accessibilityContainer]; return child; } - (NSInteger)indexOfAccessibilityElement:(id)element { - if (element == _semanticsObject) + if (element == _semanticsObject.get()) return 0; - NSMutableArray* children = [_semanticsObject children]; + NSMutableArray* children = [_semanticsObject.get() children]; for (size_t i = 0; i < [children count]; i++) { SemanticsObject* child = children[i]; if ((![child hasChildren] && child == element) || @@ -465,22 +459,22 @@ - (BOOL)isAccessibilityElement { } - (CGRect)accessibilityFrame { - return [_semanticsObject accessibilityFrame]; + return [_semanticsObject.get() accessibilityFrame]; } - (id)accessibilityContainer { if (!_bridge) { return nil; } - return ([_semanticsObject uid] == kRootNodeId) + return ([_semanticsObject.get() uid] == kRootNodeId) ? _bridge->view() - : [[_semanticsObject parent] accessibilityContainer]; + : [[_semanticsObject.get() parent] accessibilityContainer]; } #pragma mark - UIAccessibilityAction overrides - (BOOL)accessibilityScroll:(UIAccessibilityScrollDirection)direction { - return [_semanticsObject accessibilityScroll:direction]; + return [_semanticsObject.get() accessibilityScroll:direction]; } @end @@ -506,6 +500,7 @@ - (BOOL)accessibilityScroll:(UIAccessibilityScrollDirection)direction { } AccessibilityBridge::~AccessibilityBridge() { + clearState(); view_.accessibilityElements = nil; [accessibility_channel_.get() setMessageHandler:nil]; } @@ -688,6 +683,10 @@ - (BOOL)accessibilityScroll:(UIAccessibilityScrollDirection)direction { } void AccessibilityBridge::clearState() { + for (id key in objects_.get()) { + [[objects_.get() objectForKey:key].children removeAllObjects]; + // [[[objects_.get() objectForKey:key] accessibilityContainer] release]; + } [objects_ removeAllObjects]; previous_route_id_ = 0; previous_routes_.clear(); From 5b2b36e8e03bfaf8a700da0601282c7695adaf8f Mon Sep 17 00:00:00 2001 From: Dan Field Date: Fri, 16 Nov 2018 16:03:51 -0800 Subject: [PATCH 05/13] format --- .../darwin/ios/framework/Source/accessibility_bridge.mm | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm b/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm index 47091389c1829..e9f27ee3e1d37 100644 --- a/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm +++ b/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm @@ -90,7 +90,6 @@ - (instancetype)initWithSemanticsObject:(fml::WeakPtr)semantics @implementation SemanticsObject { fml::scoped_nsobject _container; std::unique_ptr> _weakFactory; - } #pragma mark - Override base class designated initializers @@ -269,8 +268,9 @@ - (CGRect)globalRect { - (id)accessibilityContainer { if ([self hasChildren] || [self uid] == kRootNodeId) { if (_container == nil) - _container.reset([[SemanticsObjectContainer alloc] initWithSemanticsObject:_weakFactory->GetWeakPtr() - bridge:[self bridge]]); + _container.reset([[SemanticsObjectContainer alloc] + initWithSemanticsObject:_weakFactory->GetWeakPtr() + bridge:[self bridge]]); return _container.get(); } if ([self parent] == nil) { From bdc28904c803a8656e0a9a286653a78d0dfe94b1 Mon Sep 17 00:00:00 2001 From: Dan Field Date: Fri, 16 Nov 2018 16:12:34 -0800 Subject: [PATCH 06/13] remove unnecessary lines from clearState --- .../darwin/ios/framework/Source/accessibility_bridge.mm | 4 ---- 1 file changed, 4 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm b/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm index e9f27ee3e1d37..de10408e8aab7 100644 --- a/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm +++ b/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm @@ -683,10 +683,6 @@ - (BOOL)accessibilityScroll:(UIAccessibilityScrollDirection)direction { } void AccessibilityBridge::clearState() { - for (id key in objects_.get()) { - [[objects_.get() objectForKey:key].children removeAllObjects]; - // [[[objects_.get() objectForKey:key] accessibilityContainer] release]; - } [objects_ removeAllObjects]; previous_route_id_ = 0; previous_routes_.clear(); From ee501e9f8ba7bee3a12862c8410ddd38c024566f Mon Sep 17 00:00:00 2001 From: Dan Field Date: Mon, 19 Nov 2018 01:31:10 -0800 Subject: [PATCH 07/13] one more leak --- .../darwin/ios/framework/Source/FlutterViewController.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm index 03fb578440220..14cd35be60f08 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm @@ -752,7 +752,7 @@ - (void)onMemoryWarning:(NSNotification*)notification { - (void)onLocaleUpdated:(NSNotification*)notification { NSArray* preferredLocales = [NSLocale preferredLanguages]; - NSMutableArray* data = [NSMutableArray new]; + NSMutableArray* data = [[NSMutableArray new] autorelease]; for (NSString* localeID in preferredLocales) { NSLocale* currentLocale = [[NSLocale alloc] initWithLocaleIdentifier:localeID]; NSString* languageCode = [currentLocale objectForKey:NSLocaleLanguageCode]; From af85eab56c74c9186ee52ce6ab248b64258da023 Mon Sep 17 00:00:00 2001 From: Dan Field Date: Mon, 19 Nov 2018 02:23:40 -0800 Subject: [PATCH 08/13] make engine shutdown testable --- fml/logging.h | 3 ++- .../darwin/ios/framework/Headers/FlutterViewController.h | 5 +++++ .../darwin/ios/framework/Source/FlutterViewController.mm | 4 ++-- .../ios/framework/Source/FlutterViewController_Internal.h | 2 +- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/fml/logging.h b/fml/logging.h index 20cb887cb20df..dc5faa59977b0 100644 --- a/fml/logging.h +++ b/fml/logging.h @@ -81,7 +81,8 @@ bool ShouldCreateLogMessage(LogSeverity severity); #ifndef NDEBUG #define FML_DLOG(severity) FML_LOG(severity) -#define FML_DCHECK(condition) FML_CHECK(condition) +#define FML_DCHECK(condition) FML_EAT_STREAM_PARAMETERS(condition) +// #define FML_DCHECK(condition) FML_CHECK(condition) #else #define FML_DLOG(severity) FML_EAT_STREAM_PARAMETERS(true) #define FML_DCHECK(condition) FML_EAT_STREAM_PARAMETERS(condition) diff --git a/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h b/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h index 49879149129c3..b320f9550a6cf 100644 --- a/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h +++ b/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h @@ -147,6 +147,11 @@ FLUTTER_EXPORT */ @property(nonatomic, getter=isViewOpaque) BOOL viewOpaque; +/** + * The `FlutterEngine` instance for this view controller. + */ +@property(weak, nonatomic, readonly) FlutterEngine* engine; + @end #endif // FLUTTER_FLUTTERVIEWCONTROLLER_H_ diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm index 14cd35be60f08..d253a74c9cf8c 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm @@ -119,8 +119,8 @@ - (void)performCommonViewControllerInitialization { [self setupNotificationCenterObservers]; } -- (fml::scoped_nsobject)engine { - return _engine; +- (FlutterEngine*)engine { + return _engine.get(); } - (fml::WeakPtr)getWeakPtr { diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h index 46705b9be8d23..fe9f0c0e478d8 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h @@ -17,7 +17,7 @@ - (fml::WeakPtr)getWeakPtr; - (shell::FlutterPlatformViewsController*)platformViewsController; -@property(readonly) fml::scoped_nsobject engine; +// @property(readonly) fml::scoped_nsobject engine; @end From b09e03c1bf4c9fb0ba2aef2c959837029200a6d3 Mon Sep 17 00:00:00 2001 From: Dan Field Date: Mon, 19 Nov 2018 02:39:31 -0800 Subject: [PATCH 09/13] revert unintentional change --- fml/logging.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/fml/logging.h b/fml/logging.h index dc5faa59977b0..20cb887cb20df 100644 --- a/fml/logging.h +++ b/fml/logging.h @@ -81,8 +81,7 @@ bool ShouldCreateLogMessage(LogSeverity severity); #ifndef NDEBUG #define FML_DLOG(severity) FML_LOG(severity) -#define FML_DCHECK(condition) FML_EAT_STREAM_PARAMETERS(condition) -// #define FML_DCHECK(condition) FML_CHECK(condition) +#define FML_DCHECK(condition) FML_CHECK(condition) #else #define FML_DLOG(severity) FML_EAT_STREAM_PARAMETERS(true) #define FML_DCHECK(condition) FML_EAT_STREAM_PARAMETERS(condition) From c10b1f9c23562327678d24d93fc436b756e85e5b Mon Sep 17 00:00:00 2001 From: Dan Field Date: Mon, 17 Dec 2018 14:06:31 -0800 Subject: [PATCH 10/13] Move logic to FlutterEngine --- .../darwin/ios/framework/Headers/Flutter.h | 3 + .../ios/framework/Headers/FlutterEngine.h | 28 +++++++-- .../Headers/FlutterHeadlessDartRunner.h | 19 +++++- .../ios/framework/Source/FlutterEngine.mm | 58 ++++++++++--------- .../Source/FlutterHeadlessDartRunner.mm | 11 +++- .../framework/Source/FlutterViewController.mm | 8 +-- 6 files changed, 86 insertions(+), 41 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Headers/Flutter.h b/shell/platform/darwin/ios/framework/Headers/Flutter.h index 364912afc04a8..9135c8200603c 100644 --- a/shell/platform/darwin/ios/framework/Headers/Flutter.h +++ b/shell/platform/darwin/ios/framework/Headers/Flutter.h @@ -8,6 +8,9 @@ /** BREAKING CHANGES: + December 17, 2018: + - Changed designated initializer on FlutterEngine + October 5, 2018: - Removed FlutterNavigationController.h/.mm - Changed return signature of `FlutterDartHeadlessCodeRunner.run*` from void diff --git a/shell/platform/darwin/ios/framework/Headers/FlutterEngine.h b/shell/platform/darwin/ios/framework/Headers/FlutterEngine.h index bf22f78a05ae7..da7b869388ccd 100644 --- a/shell/platform/darwin/ios/framework/Headers/FlutterEngine.h +++ b/shell/platform/darwin/ios/framework/Headers/FlutterEngine.h @@ -56,7 +56,28 @@ FLUTTER_EXPORT * @param projectOrNil The `FlutterDartProject` to run. */ - (instancetype)initWithName:(NSString*)labelPrefix - project:(FlutterDartProject*)projectOrNil NS_DESIGNATED_INITIALIZER; + project:(FlutterDartProject*)projectOrNil; + +/** + * Initialize this FlutterEngine with a `FlutterDartProject`. + * + * If the FlutterDartProject is not specified, the FlutterEngine will attempt to locate + * the project in a default location (the flutter_assets folder in the iOS application + * bundle). + * + * A newly initialized engine will not run the `FlutterDartProject` until either + * `-runWithEntrypoint:` or `-runWithEntrypoint:libraryURI:` is called. + * + * @param labelPrefix The label prefix used to identify threads for this instance. Should + * be unique across FlutterEngine instances, and is used in instrumentation to label + * the threads used by this FlutterEngine. + * @param projectOrNil The `FlutterDartProject` to run. + * @param allowHeadlessExecution Whether or not to allow this instance to continue + * running after passing a nil `FlutterViewController` to `-setViewController:`. + */ +- (instancetype)initWithName:(NSString*)labelPrefix + project:(FlutterDartProject*)projectOrNil + allowHeadlessExecution:(BOOL)allowHeadlessExecution NS_DESIGNATED_INITIALIZER; /** * The default initializer is not available for this object. @@ -97,11 +118,6 @@ FLUTTER_EXPORT */ - (BOOL)runWithEntrypoint:(NSString*)entrypoint libraryURI:(NSString*)uri; -/** - * Shuts down the engine if it's running. - */ -- (void)shutdown; - /** * Sets the `FlutterViewController` for this instance. The FlutterEngine must be * running (e.g. a successful call to `-runWithEntrypoint:` or `-runWithEntrypoint:libraryURI`) diff --git a/shell/platform/darwin/ios/framework/Headers/FlutterHeadlessDartRunner.h b/shell/platform/darwin/ios/framework/Headers/FlutterHeadlessDartRunner.h index e51dc6d5068c0..24acbc08ad6fe 100644 --- a/shell/platform/darwin/ios/framework/Headers/FlutterHeadlessDartRunner.h +++ b/shell/platform/darwin/ios/framework/Headers/FlutterHeadlessDartRunner.h @@ -46,8 +46,25 @@ FLUTTER_DEPRECATED("FlutterEngine should be used rather than FlutterHeadlessDart * be unique across FlutterEngine instances * @param projectOrNil The `FlutterDartProject` to run. */ +- (instancetype)initWithName:(NSString*)labelPrefix project:(FlutterDartProject*)projectOrNil; + +/** + * Iniitalize this FlutterHeadlessDartRunner with a `FlutterDartProject`. + * + * If the FlutterDartProject is not specified, the FlutterHeadlessDartRunner will attempt to locate + * the project in a default location. + * + * A newly initialized engine will not run the `FlutterDartProject` until either + * `-runWithEntrypoint:` or `-runWithEntrypoint:libraryURI` is called. + * + * @param labelPrefix The label prefix used to identify threads for this instance. Should + * be unique across FlutterEngine instances + * @param projectOrNil The `FlutterDartProject` to run. + * @param allowHeadlessExecution Must be set to `YES`. + */ - (instancetype)initWithName:(NSString*)labelPrefix - project:(FlutterDartProject*)projectOrNil NS_DESIGNATED_INITIALIZER; + project:(FlutterDartProject*)projectOrNil + allowHeadlessExecution:(BOOL)allowHeadlessExecution NS_DESIGNATED_INITIALIZER; /** * Not recommended for use - will initialize with a default label ("io.flutter.headless") diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm index dd6a0b22b197b..587cb4df1563b 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm @@ -60,12 +60,22 @@ @implementation FlutterEngine { fml::scoped_nsobject _settingsChannel; int64_t _nextTextureId; + + BOOL _allowHeadlessExecution; } - (instancetype)initWithName:(NSString*)labelPrefix project:(FlutterDartProject*)projectOrNil { + return [self initWithName:labelPrefix project:projectOrNil allowHeadlessExecution:YES]; +} + +- (instancetype)initWithName:(NSString*)labelPrefix + project:(FlutterDartProject*)projectOrNil + allowHeadlessExecution:(BOOL)allowHeadlessExecution { self = [super init]; NSAssert(self, @"Super init cannot be nil"); NSAssert(labelPrefix, @"labelPrefix is required"); + + _allowHeadlessExecution = allowHeadlessExecution; _labelPrefix = [labelPrefix copy]; _weakFactory = std::make_unique>(self); @@ -134,7 +144,14 @@ - (void)setViewController:(FlutterViewController*)viewController { FML_DCHECK(self.iosPlatformView); _viewController = [viewController getWeakPtr]; self.iosPlatformView->SetOwnerViewController(_viewController); - [self maybeSetupPlatformViewChannels]; + if (!viewController && !_allowHeadlessExecution) { + [self resetChannels]; + + _shell.reset(); + _threadHost.Reset(); + } else { + [self maybeSetupPlatformViewChannels]; + } } - (FlutterViewController*)viewController { @@ -175,6 +192,20 @@ - (FlutterBasicMessageChannel*)settingsChannel { return _settingsChannel.get(); } +- (void)resetChannels { + _localizationChannel.reset(); + _navigationChannel.reset(); + _platformChannel.reset(); + _platformViewsChannel.reset(); + _textInputChannel.reset(); + _lifecycleChannel.reset(); + _systemChannel.reset(); + _settingsChannel.reset(); +} + +// If you add a channel, be sure to also update `resetChannels`. +// Channels get a reference to the engine, and therefore need manual +// cleanup for proper collection. - (void)setupChannels { _localizationChannel.reset([[FlutterMethodChannel alloc] initWithName:@"flutter/localization" @@ -356,31 +387,6 @@ - (BOOL)createShell:(NSString*)entrypoint libraryURI:(NSString*)libraryURI { return _shell != nullptr; } -- (void)shutdown { - if (_shell == nullptr) { - return; - } - [self setViewController:nil]; - - _publisher.reset(); - - _platformViewsController.reset(); - - _platformPlugin.reset(); - _textInputPlugin.reset(); - _localizationChannel.reset(); - _navigationChannel.reset(); - _platformChannel.reset(); - _platformViewsChannel.reset(); - _textInputChannel.reset(); - _lifecycleChannel.reset(); - _systemChannel.reset(); - _settingsChannel.reset(); - - _shell.reset(); - _threadHost.Reset(); -} - - (BOOL)runWithEntrypoint:(NSString*)entrypoint libraryURI:(NSString*)libraryURI { if ([self createShell:entrypoint libraryURI:libraryURI]) { [self launchEngine:entrypoint libraryURI:libraryURI]; diff --git a/shell/platform/darwin/ios/framework/Source/FlutterHeadlessDartRunner.mm b/shell/platform/darwin/ios/framework/Source/FlutterHeadlessDartRunner.mm index 8e7cb26d48a1d..b3da7a037a5dc 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterHeadlessDartRunner.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterHeadlessDartRunner.mm @@ -28,7 +28,16 @@ @implementation FlutterHeadlessDartRunner { } - (instancetype)initWithName:(NSString*)labelPrefix project:(FlutterDartProject*)projectOrNil { - return [super initWithName:labelPrefix project:projectOrNil]; + return [self initWithName:labelPrefix project:projectOrNil allowHeadlessExecution:YES]; +} + +- (instancetype)initWithName:(NSString*)labelPrefix + project:(FlutterDartProject*)projectOrNil + allowHeadlessExecution:(BOOL)allowHeadlessExecution { + NSAssert(allowHeadlessExecution == YES, @"Cannot initialize a FlutterHeadlessDartRunner without headless execution."); + return [super initWithName:labelPrefix + project:projectOrNil + allowHeadlessExecution:allowHeadlessExecution]; } - (instancetype)init { return [self initWithName:@"io.flutter.headless" project:nil]; diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm index 918bc658f9706..7c0e432b8317b 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm @@ -37,7 +37,6 @@ @implementation FlutterViewController { BOOL _initialized; BOOL _viewOpaque; BOOL _engineNeedsLaunch; - BOOL _engineIsOwnedByMe; } #pragma mark - Manage and override all designated initializers @@ -51,7 +50,6 @@ - (instancetype)initWithEngine:(FlutterEngine*)engine _viewOpaque = YES; _engine.reset([engine retain]); _engineNeedsLaunch = NO; - _engineIsOwnedByMe = NO; _flutterView.reset([[FlutterView alloc] initWithDelegate:_engine opaque:self.isViewOpaque]); _weakFactory = std::make_unique>(self); @@ -69,11 +67,10 @@ - (instancetype)initWithProject:(FlutterDartProject*)projectOrNil if (self) { _viewOpaque = YES; _weakFactory = std::make_unique>(self); - _engine.reset([[FlutterEngine alloc] initWithName:@"io.flutter" project:projectOrNil]); + _engine.reset([[FlutterEngine alloc] initWithName:@"io.flutter" project:projectOrNil allowHeadlessExecution:NO]); _flutterView.reset([[FlutterView alloc] initWithDelegate:_engine opaque:self.isViewOpaque]); [_engine.get() createShell:nil libraryURI:nil]; _engineNeedsLaunch = YES; - _engineIsOwnedByMe = YES; [self loadDefaultSplashScreenView]; [self performCommonViewControllerInitialization]; } @@ -432,9 +429,6 @@ - (void)viewDidDisappear:(BOOL)animated { - (void)dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self]; - if (_engineIsOwnedByMe) { - [_engine.get() shutdown]; - } [super dealloc]; } From e221b0cf14ab13ec7b49f681dfd8cf3b966e53f7 Mon Sep 17 00:00:00 2001 From: Dan Field Date: Mon, 17 Dec 2018 14:36:30 -0800 Subject: [PATCH 11/13] format --- shell/platform/darwin/ios/framework/Headers/FlutterEngine.h | 5 ++--- .../darwin/ios/framework/Source/FlutterHeadlessDartRunner.mm | 3 ++- .../darwin/ios/framework/Source/FlutterViewController.mm | 4 +++- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Headers/FlutterEngine.h b/shell/platform/darwin/ios/framework/Headers/FlutterEngine.h index da7b869388ccd..21a36cdd6fff2 100644 --- a/shell/platform/darwin/ios/framework/Headers/FlutterEngine.h +++ b/shell/platform/darwin/ios/framework/Headers/FlutterEngine.h @@ -55,8 +55,7 @@ FLUTTER_EXPORT * the threads used by this FlutterEngine. * @param projectOrNil The `FlutterDartProject` to run. */ -- (instancetype)initWithName:(NSString*)labelPrefix - project:(FlutterDartProject*)projectOrNil; +- (instancetype)initWithName:(NSString*)labelPrefix project:(FlutterDartProject*)projectOrNil; /** * Initialize this FlutterEngine with a `FlutterDartProject`. @@ -77,7 +76,7 @@ FLUTTER_EXPORT */ - (instancetype)initWithName:(NSString*)labelPrefix project:(FlutterDartProject*)projectOrNil - allowHeadlessExecution:(BOOL)allowHeadlessExecution NS_DESIGNATED_INITIALIZER; + allowHeadlessExecution:(BOOL)allowHeadlessExecution NS_DESIGNATED_INITIALIZER; /** * The default initializer is not available for this object. diff --git a/shell/platform/darwin/ios/framework/Source/FlutterHeadlessDartRunner.mm b/shell/platform/darwin/ios/framework/Source/FlutterHeadlessDartRunner.mm index b3da7a037a5dc..6299e9e684474 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterHeadlessDartRunner.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterHeadlessDartRunner.mm @@ -34,7 +34,8 @@ - (instancetype)initWithName:(NSString*)labelPrefix project:(FlutterDartProject* - (instancetype)initWithName:(NSString*)labelPrefix project:(FlutterDartProject*)projectOrNil allowHeadlessExecution:(BOOL)allowHeadlessExecution { - NSAssert(allowHeadlessExecution == YES, @"Cannot initialize a FlutterHeadlessDartRunner without headless execution."); + NSAssert(allowHeadlessExecution == YES, + @"Cannot initialize a FlutterHeadlessDartRunner without headless execution."); return [super initWithName:labelPrefix project:projectOrNil allowHeadlessExecution:allowHeadlessExecution]; diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm index 7c0e432b8317b..7f2b83cd38fcc 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm @@ -67,7 +67,9 @@ - (instancetype)initWithProject:(FlutterDartProject*)projectOrNil if (self) { _viewOpaque = YES; _weakFactory = std::make_unique>(self); - _engine.reset([[FlutterEngine alloc] initWithName:@"io.flutter" project:projectOrNil allowHeadlessExecution:NO]); + _engine.reset([[FlutterEngine alloc] initWithName:@"io.flutter" + project:projectOrNil + allowHeadlessExecution:NO]); _flutterView.reset([[FlutterView alloc] initWithDelegate:_engine opaque:self.isViewOpaque]); [_engine.get() createShell:nil libraryURI:nil]; _engineNeedsLaunch = YES; From d8c889b21fef527c8515a8f6deff6c13e561b485 Mon Sep 17 00:00:00 2001 From: Dan Field Date: Tue, 18 Dec 2018 08:58:50 -0800 Subject: [PATCH 12/13] remove comment --- .../ios/framework/Source/FlutterViewController_Internal.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h index fe9f0c0e478d8..4908c7dc80638 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h @@ -17,8 +17,6 @@ - (fml::WeakPtr)getWeakPtr; - (shell::FlutterPlatformViewsController*)platformViewsController; -// @property(readonly) fml::scoped_nsobject engine; - @end #endif // FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_FLUTTERVIEWCONTROLLER_INTERNAL_H_ From e86a53f99be71ed2e997074feae19b895d35bac5 Mon Sep 17 00:00:00 2001 From: Dan Field Date: Mon, 14 Jan 2019 14:50:00 -0800 Subject: [PATCH 13/13] scoped_nsobject --- .../framework/Source/accessibility_bridge.mm | 24 +++++++++---------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm b/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm index de10408e8aab7..e425666af0cdf 100644 --- a/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm +++ b/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm @@ -82,14 +82,13 @@ @implementation FlutterCustomAccessibilityAction { */ @interface SemanticsObjectContainer : NSObject - (instancetype)init __attribute__((unavailable("Use initWithSemanticsObject instead"))); -- (instancetype)initWithSemanticsObject:(fml::WeakPtr)semanticsObject +- (instancetype)initWithSemanticsObject:(SemanticsObject*)semanticsObject bridge:(fml::WeakPtr)bridge NS_DESIGNATED_INITIALIZER; @end @implementation SemanticsObject { fml::scoped_nsobject _container; - std::unique_ptr> _weakFactory; } #pragma mark - Override base class designated initializers @@ -111,8 +110,7 @@ - (instancetype)initWithBridge:(fml::WeakPtr)bridge if (self) { _bridge = bridge; _uid = uid; - self.children = [[[NSMutableArray alloc] init] autorelease]; - _weakFactory = std::make_unique>(self); + _children = [[NSMutableArray alloc] init]; } return self; @@ -123,7 +121,7 @@ - (void)dealloc { child.parent = nil; } [_children removeAllObjects]; - [_children dealloc]; + [_children release]; _parent = nil; [super dealloc]; } @@ -268,9 +266,8 @@ - (CGRect)globalRect { - (id)accessibilityContainer { if ([self hasChildren] || [self uid] == kRootNodeId) { if (_container == nil) - _container.reset([[SemanticsObjectContainer alloc] - initWithSemanticsObject:_weakFactory->GetWeakPtr() - bridge:[self bridge]]); + _container.reset([[SemanticsObjectContainer alloc] initWithSemanticsObject:self + bridge:[self bridge]]); return _container.get(); } if ([self parent] == nil) { @@ -396,7 +393,7 @@ - (UIAccessibilityTraits)accessibilityTraits { @end @implementation SemanticsObjectContainer { - fml::WeakPtr _semanticsObject; + fml::scoped_nsobject _semanticsObject; fml::WeakPtr _bridge; } @@ -409,13 +406,13 @@ - (instancetype)init { return nil; } -- (instancetype)initWithSemanticsObject:(fml::WeakPtr)semanticsObject +- (instancetype)initWithSemanticsObject:(SemanticsObject*)semanticsObject bridge:(fml::WeakPtr)bridge { - FML_DCHECK(semanticsObject.get() != nil) << "semanticsObject must be set"; + FML_DCHECK(semanticsObject) << "semanticsObject must be set"; self = [super init]; if (self) { - _semanticsObject = semanticsObject; + _semanticsObject.reset([semanticsObject retain]); _bridge = bridge; } @@ -431,8 +428,9 @@ - (NSInteger)accessibilityElementCount { - (nullable id)accessibilityElementAtIndex:(NSInteger)index { if (index < 0 || index >= [self accessibilityElementCount]) return nil; - if (index == 0) + if (index == 0) { return _semanticsObject.get(); + } SemanticsObject* child = [_semanticsObject.get() children][index - 1]; if ([child hasChildren]) return [child accessibilityContainer];