Revert "Reland "iOS: Migrate FlutterEngine to ARC" (#55937)" (flutter/engine#55954)

There are still a couple closures where on engine shutdown, the last
live reference to FlutterEngine may be on a thread other than the
platform thread.

Specifically, the profiling data capture callback can result in the last
live reference to a FlutterEngine being on a profiling thread, resulting
in an assertion failure in the destructor of the PlatformViewsController
held by FlutterEngine, because PlatformViewsController holds a
WeakPtrFactory whose destructor asserts that it be on the platform
thread.

See:

f0f29ce37f/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm (L511-L515)

Backtrace of such a crash:
```
=================================================================
Main Thread Checker: UI API called on a background thread: -[UIView removeFromSuperview]
PID: 46919, TID: 333147, Thread name: FlutterEngine.166.profiler, Queue name: com.apple.root.default-qos.overcommit, QoS: 0
Backtrace:
4   libios_test_flutter.dylib           0x000000014b658aed -[FlutterTextInputPlugin dealloc] + 27
5   libobjc.A.dylib                     0x00007ff800055228 _ZN11objc_object17sidetable_releaseEbb + 204
6   libios_test_flutter.dylib           0x000000014b63df5a -[FlutterEngine .cxx_destruct] + 135
7   libobjc.A.dylib                     0x00007ff800035766 _ZL27object_cxxDestructFromClassP11objc_objectP10objc_class + 83
8   libobjc.A.dylib                     0x00007ff80004ddfc objc_destructInstance + 61
9   CoreFoundation                      0x00007ff8004a286b -[NSObject(NSObject) __dealloc_zombie] + 159
10  libios_test_flutter.dylib           0x000000014b635fea -[FlutterEngine dealloc] + 334
11  libobjc.A.dylib                     0x00007ff800055228 _ZN11objc_object17sidetable_releaseEbb + 204
12  libios_test_flutter.dylib           0x000000014b63eda9 _ZNSt3_fl10__function6__funcIZ30-[FlutterEngine startProfiler]E3$_0NS_9allocatorIS2_EEFN7flutter13ProfileSampleEvEEclEv + 69
13  libios_test_flutter.dylib           0x000000014bcd8eaa _ZNSt3_fl10__function6__funcIZNK7flutter16SamplingProfiler16SampleRepeatedlyEN3fml9TimeDeltaEE3$_0NS_9allocatorIS6_EEFvvEEclEv + 40
14  libios_test_flutter.dylib           0x000000014b868786 _ZN3fml15MessageLoopImpl10FlushTasksENS_9FlushTypeE + 156
15  libios_test_flutter.dylib           0x000000014b86ecca _ZN3fml17MessageLoopDarwin11OnTimerFireEP16__CFRunLoopTimerPS0_ + 26
16  CoreFoundation                      0x00007ff8003ea4a5 __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ + 20
17  CoreFoundation                      0x00007ff8003ea032 __CFRunLoopDoTimer + 801
18  CoreFoundation                      0x00007ff8003e97b6 __CFRunLoopDoTimers + 243
19  CoreFoundation                      0x00007ff8003e4028 __CFRunLoopRun + 2108
20  CoreFoundation                      0x00007ff8003e3409 CFRunLoopRunSpecific + 557
21  libios_test_flutter.dylib           0x000000014b86ee07 _ZN3fml17MessageLoopDarwin3RunEv + 65
22  libios_test_flutter.dylib           0x000000014b8686a4 _ZN3fml15MessageLoopImpl5DoRunEv + 22
23  libios_test_flutter.dylib           0x000000014b86dce5 _ZNSt3_fl10__function6__funcIZN3fml6ThreadC1ERKNS_8functionIFvRKNS3_12ThreadConfigEEEES7_E3$_0NS_9allocatorISC_EEFvvEEclEv + 135
24  libios_test_flutter.dylib           0x000000014b86da87 _ZZN3fml12ThreadHandleC1EONSt3_fl8functionIFvvEEEEN3$_08__invokeEPv + 27
25  libsystem_pthread.dylib             0x000000010d6a818b _pthread_start + 99
26  libsystem_pthread.dylib             0x000000010d6a3ae3 thread_start + 15
2024-10-18 09:31:27.549111-0700 IosUnitTests[46919:333147] [reports] Main Thread Checker: UI API called on a background thread: -[UIView removeFromSuperview]
PID: 46919, TID: 333147, Thread name: FlutterEngine.166.profiler, Queue name: com.apple.root.default-qos.overcommit, QoS: 0
Backtrace:
4   libios_test_flutter.dylib           0x000000014b658aed -[FlutterTextInputPlugin dealloc] + 27
5   libobjc.A.dylib                     0x00007ff800055228 _ZN11objc_object17sidetable_releaseEbb + 204
6   libios_test_flutter.dylib           0x000000014b63df5a -[FlutterEngine .cxx_destruct] + 135
7   libobjc.A.dylib                     0x00007ff800035766 _ZL27object_cxxDestructFromClassP11objc_objectP10objc_class + 83
8   libobjc.A.dylib                     0x00007ff80004ddfc objc_destructInstance + 61
9   CoreFoundation                      0x00007ff8004a286b -[NSObject(NSObject) __dealloc_zombie] + 159
10  libios_test_flutter.dylib           0x000000014b635fea -[FlutterEngine dealloc] + 334
11  libobjc.A.dylib                     0x00007ff800055228 _ZN11objc_object17sidetable_releaseEbb + 204
12  libios_test_flutter.dylib           0x000000014b63eda9 _ZNSt3_fl10__function6__funcIZ30-[FlutterEngine startProfiler]E3$_0NS_9allocatorIS2_EEFN7flutter13ProfileSampleEvEEclEv + 69
13  libios_test_flutter.dylib           0x000000014bcd8eaa _ZNSt3_fl10__function6__funcIZNK7flutter16SamplingProfiler16SampleRepeatedlyEN3fml9TimeDeltaEE3$_0NS_9allocatorIS6_EEFvvEEclEv + 40
14  libios_test_flutter.dylib           0x000000014b868786 _ZN3fml15MessageLoopImpl10FlushTasksENS_9FlushTypeE + 156
15  libios_test_flutter.dylib           0x000000014b86ecca _ZN3fml17MessageLoopDarwin11OnTimerFireEP16__CFRunLoopTimerPS0_ + 26
16  CoreFoundation                      0x00007ff8003ea4a5 __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ + 20
17  CoreFoundation                      0x00007ff8003ea032 __CFRunLoopDoTimer + 801
18  CoreFoundation                      0x00007ff8003e97b6 __CFRunLoopDoTimers + 243
19  CoreFoundation                      0x00007ff8003e4028 __CFRunLoopRun + 2108
20  CoreFoundation                      0x00007ff8003e3409 CFRunLoopRunSpecific + 557
21  libios_test_flutter.dylib           0x000000014b86ee07 _ZN3fml17MessageLoopDarwin3RunEv + 65
22  libios_test_flutter.dylib           0x000000014b8686a4 _ZN3fml15MessageLoopImpl5DoRunEv + 22
23  libios_test_flutter.dylib           0x000000014b86dce5 _ZNSt3_fl10__function6__funcIZN3fml6ThreadC1ERKNS_8functionIFvRKNS3_12ThreadConfigEEEES7_E3$_0NS_9allocatorISC_EEFvvEEclEv + 135
24  libios_test_flutter.dylib           0x000000014b86da87 _ZZN3fml12ThreadHandleC1EONSt3_fl8functionIFvvEEEEN3$_08__invokeEPv + 27
25  libsystem_pthread.dylib             0x000000010d6a818b _pthread_start + 99
26  libsystem_pthread.dylib             0x000000010d6a3ae3 thread_start + 15
IosUnitTests(47009,0x10ec76240) malloc: enabling scribbling to detect mods to free blocks
IosUnitTests(47009) MallocStackLogging: could not tag MSL-related memory as no_footprint, so those pages will be included in process footprint - (null)
IosUnitTests(47009) MallocStackLogging: stack logs being written to /private/tmp/stack-logs.47009.1057b6000.IosUnitTests.0o4HgN.index
IosUnitTests(47009) MallocStackLogging: recording malloc and VM allocation stacks to disk using standard recorder
IosUnitTests(47009) MallocStackLogging: process 46919 no longer exists, stack logs deleted from /tmp/stack-logs.46919.10d1fc000.IosUnitTests.kFionm.index
```

This reverts commit 530a9f896c3547fd5e27539e9049fae392d61e73.

## Pre-launch Checklist

- [X] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [X] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [X] I read and followed the [Flutter Style Guide] and the [C++,
Objective-C, Java style guides].
- [X] I listed at least one issue that this PR fixes in the description
above.
- [X] I added new tests to check the change I am making or feature I am
adding, or the PR is [test-exempt]. See [testing the engine] for
instructions on writing and running engine tests.
- [X] I updated/added relevant documentation (doc comments with `///`).
- [X] I signed the [CLA].
- [X] All existing and new tests are passing.

If you need help, consider asking for advice on the #hackers-new channel
on [Discord].

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/wiki/Tree-hygiene#overview
[Tree Hygiene]: https://github.com/flutter/flutter/wiki/Tree-hygiene
[test-exempt]:
https://github.com/flutter/flutter/wiki/Tree-hygiene#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo
[C++, Objective-C, Java style guides]:
https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style
[testing the engine]:
https://github.com/flutter/flutter/wiki/Testing-the-engine
[CLA]: https://cla.developers.google.com/
[flutter/tests]: https://github.com/flutter/tests
[breaking change policy]:
https://github.com/flutter/flutter/wiki/Tree-hygiene#handling-breaking-changes
[Discord]: https://github.com/flutter/flutter/wiki/Chat
This commit is contained in:
Chris Bracken 2024-10-18 11:36:30 -07:00 committed by GitHub
parent cf61b19161
commit 0a9ed47633
6 changed files with 364 additions and 278 deletions

View File

@ -70,9 +70,7 @@ source_set("flutter_framework_source_arc") {
"framework/Source/FlutterDartVMServicePublisher.mm",
"framework/Source/FlutterEmbedderKeyResponder.h",
"framework/Source/FlutterEmbedderKeyResponder.mm",
"framework/Source/FlutterEngine.mm",
"framework/Source/FlutterEngineGroup.mm",
"framework/Source/FlutterEngine_Internal.h",
"framework/Source/FlutterHeadlessDartRunner.mm",
"framework/Source/FlutterKeyPrimaryResponder.h",
"framework/Source/FlutterKeySecondaryResponder.h",
@ -188,6 +186,8 @@ source_set("flutter_framework_source") {
# iOS embedder is migrating to ARC.
# New files are highly encouraged to be in ARC.
# To add new files in ARC, add them to the `flutter_framework_source_arc` target.
"framework/Source/FlutterEngine.mm",
"framework/Source/FlutterEngine_Internal.h",
"framework/Source/FlutterViewController.mm",
"framework/Source/FlutterViewController_Internal.h",
"platform_view_ios.h",

View File

@ -42,8 +42,6 @@
#import "flutter/shell/platform/darwin/ios/rendering_api_selection.h"
#include "flutter/shell/profiling/sampling_profiler.h"
FLUTTER_ASSERT_ARC
/// Inheriting ThreadConfigurer and use iOS platform thread API to configure the thread priorities
/// Using iOS platform thread API to configure thread priority
static void IOSPlatformThreadConfigSetter(const fml::Thread::ThreadConfig& config) {
@ -90,7 +88,7 @@ NSString* const kFlutterKeyDataChannel = @"flutter/keydata";
static constexpr int kNumProfilerSamplesPerSec = 5;
@interface FlutterEngineRegistrar : NSObject <FlutterPluginRegistrar>
@property(nonatomic, weak) FlutterEngine* flutterEngine;
@property(nonatomic, assign) FlutterEngine* flutterEngine;
- (instancetype)initWithPlugin:(NSString*)pluginKey flutterEngine:(FlutterEngine*)flutterEngine;
@end
@ -99,14 +97,6 @@ static constexpr int kNumProfilerSamplesPerSec = 5;
FlutterTextInputDelegate,
FlutterBinaryMessenger,
FlutterTextureRegistry>
#pragma mark - Properties
@property(nonatomic, readonly) FlutterDartProject* dartProject;
@property(nonatomic, readonly, copy) NSString* labelPrefix;
@property(nonatomic, readonly, assign) BOOL allowHeadlessExecution;
@property(nonatomic, readonly, assign) BOOL restorationEnabled;
// Maintains a dictionary of plugin names that have registered with the engine. Used by
// FlutterEngineRegistrar to implement a FlutterPluginRegistrar.
@property(nonatomic, readonly) NSMutableDictionary* pluginPublications;
@ -114,53 +104,55 @@ static constexpr int kNumProfilerSamplesPerSec = 5;
@property(nonatomic, readwrite, copy) NSString* isolateId;
@property(nonatomic, copy) NSString* initialRoute;
@property(nonatomic, strong) id<NSObject> flutterViewControllerWillDeallocObserver;
@property(nonatomic, strong) FlutterDartVMServicePublisher* publisher;
@property(nonatomic, assign) int64_t nextTextureId;
#pragma mark - Channel properties
@property(nonatomic, strong) FlutterPlatformPlugin* platformPlugin;
@property(nonatomic, strong) FlutterTextInputPlugin* textInputPlugin;
@property(nonatomic, strong) FlutterUndoManagerPlugin* undoManagerPlugin;
@property(nonatomic, strong) FlutterSpellCheckPlugin* spellCheckPlugin;
@property(nonatomic, strong) FlutterRestorationPlugin* restorationPlugin;
@property(nonatomic, strong) FlutterMethodChannel* localizationChannel;
@property(nonatomic, strong) FlutterMethodChannel* navigationChannel;
@property(nonatomic, strong) FlutterMethodChannel* restorationChannel;
@property(nonatomic, strong) FlutterMethodChannel* platformChannel;
@property(nonatomic, strong) FlutterMethodChannel* platformViewsChannel;
@property(nonatomic, strong) FlutterMethodChannel* textInputChannel;
@property(nonatomic, strong) FlutterMethodChannel* undoManagerChannel;
@property(nonatomic, strong) FlutterMethodChannel* scribbleChannel;
@property(nonatomic, strong) FlutterMethodChannel* spellCheckChannel;
@property(nonatomic, strong) FlutterBasicMessageChannel* lifecycleChannel;
@property(nonatomic, strong) FlutterBasicMessageChannel* systemChannel;
@property(nonatomic, strong) FlutterBasicMessageChannel* settingsChannel;
@property(nonatomic, strong) FlutterBasicMessageChannel* keyEventChannel;
@property(nonatomic, strong) FlutterMethodChannel* screenshotChannel;
@property(nonatomic, retain) id<NSObject> flutterViewControllerWillDeallocObserver;
#pragma mark - Embedder API properties
@property(nonatomic, assign) BOOL enableEmbedderAPI;
// Function pointers for interacting with the embedder.h API.
@property(nonatomic) FlutterEngineProcTable& embedderAPI;
@end
@implementation FlutterEngine {
fml::scoped_nsobject<FlutterDartProject> _dartProject;
std::shared_ptr<flutter::ThreadHost> _threadHost;
std::unique_ptr<flutter::Shell> _shell;
NSString* _labelPrefix;
std::unique_ptr<fml::WeakNSObjectFactory<FlutterEngine>> _weakFactory;
// TODO(cbracken): https://github.com/flutter/flutter/issues/155943
// Migrate to @property(nonatomic, weak).
fml::WeakNSObject<FlutterViewController> _viewController;
fml::scoped_nsobject<FlutterDartVMServicePublisher> _publisher;
std::shared_ptr<flutter::PlatformViewsController> _platformViewsController;
flutter::IOSRenderingAPI _renderingApi;
std::shared_ptr<flutter::ProfilerMetricsIOS> _profiler_metrics;
std::shared_ptr<flutter::SamplingProfiler> _profiler;
// Channels
fml::scoped_nsobject<FlutterPlatformPlugin> _platformPlugin;
fml::scoped_nsobject<FlutterTextInputPlugin> _textInputPlugin;
fml::scoped_nsobject<FlutterUndoManagerPlugin> _undoManagerPlugin;
fml::scoped_nsobject<FlutterSpellCheckPlugin> _spellCheckPlugin;
fml::scoped_nsobject<FlutterRestorationPlugin> _restorationPlugin;
fml::scoped_nsobject<FlutterMethodChannel> _localizationChannel;
fml::scoped_nsobject<FlutterMethodChannel> _navigationChannel;
fml::scoped_nsobject<FlutterMethodChannel> _restorationChannel;
fml::scoped_nsobject<FlutterMethodChannel> _platformChannel;
fml::scoped_nsobject<FlutterMethodChannel> _platformViewsChannel;
fml::scoped_nsobject<FlutterMethodChannel> _textInputChannel;
fml::scoped_nsobject<FlutterMethodChannel> _undoManagerChannel;
fml::scoped_nsobject<FlutterMethodChannel> _scribbleChannel;
fml::scoped_nsobject<FlutterMethodChannel> _spellCheckChannel;
fml::scoped_nsobject<FlutterBasicMessageChannel> _lifecycleChannel;
fml::scoped_nsobject<FlutterBasicMessageChannel> _systemChannel;
fml::scoped_nsobject<FlutterBasicMessageChannel> _settingsChannel;
fml::scoped_nsobject<FlutterBasicMessageChannel> _keyEventChannel;
fml::scoped_nsobject<FlutterMethodChannel> _screenshotChannel;
int64_t _nextTextureId;
BOOL _allowHeadlessExecution;
BOOL _restorationEnabled;
FlutterBinaryMessengerRelay* _binaryMessenger;
FlutterTextureRegistryRelay* _textureRegistry;
std::unique_ptr<flutter::ConnectionCollection> _connections;
@ -198,21 +190,29 @@ static constexpr int kNumProfilerSamplesPerSec = 5;
_restorationEnabled = restorationEnabled;
_allowHeadlessExecution = allowHeadlessExecution;
_labelPrefix = [labelPrefix copy];
_dartProject = project ?: [[FlutterDartProject alloc] init];
_enableEmbedderAPI = _dartProject.settings.enable_embedder_api;
_weakFactory = std::make_unique<fml::WeakNSObjectFactory<FlutterEngine>>(self);
if (project == nil) {
_dartProject.reset([[FlutterDartProject alloc] init]);
} else {
_dartProject.reset([project retain]);
}
_enableEmbedderAPI = _dartProject.get().settings.enable_embedder_api;
if (_enableEmbedderAPI) {
NSLog(@"============== iOS: enable_embedder_api is on ==============");
_embedderAPI.struct_size = sizeof(FlutterEngineProcTable);
FlutterEngineGetProcAddresses(&_embedderAPI);
}
if (!EnableTracingIfNecessary(_dartProject.settings)) {
if (!EnableTracingIfNecessary([_dartProject.get() settings])) {
NSLog(
@"Cannot create a FlutterEngine instance in debug mode without Flutter tooling or "
@"Xcode.\n\nTo launch in debug mode in iOS 14+, run flutter run from Flutter tools, run "
@"from an IDE with a Flutter IDE plugin or run the iOS project from Xcode.\nAlternatively "
@"profile and release mode apps can be launched from the home screen.");
[self release];
return nil;
}
@ -292,22 +292,35 @@ static constexpr int kNumProfilerSamplesPerSec = 5;
object:self
userInfo:nil];
// nil out weak references.
// TODO(cbracken): https://github.com/flutter/flutter/issues/156222
// Ensure that FlutterEngineRegistrar is using weak pointers, then eliminate this code.
// It will be destroyed and invalidate its weak pointers
// before any other members are destroyed.
_weakFactory.reset();
/// nil out weak references.
[_registrars
enumerateKeysAndObjectsUsingBlock:^(id key, FlutterEngineRegistrar* registrar, BOOL* stop) {
registrar.flutterEngine = nil;
}];
[_labelPrefix release];
[_initialRoute release];
[_pluginPublications release];
[_registrars release];
_binaryMessenger.parent = nil;
_textureRegistry.parent = nil;
[_binaryMessenger release];
[_textureRegistry release];
_textureRegistry = nil;
[_isolateId release];
NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
if (_flutterViewControllerWillDeallocObserver) {
[center removeObserver:_flutterViewControllerWillDeallocObserver];
[_flutterViewControllerWillDeallocObserver release];
}
[center removeObserver:self];
[super dealloc];
}
- (flutter::Shell&)shell {
@ -315,6 +328,10 @@ static constexpr int kNumProfilerSamplesPerSec = 5;
return *_shell;
}
- (fml::WeakNSObject<FlutterEngine>)getWeakNSObject {
return _weakFactory->GetWeakNSObject();
}
- (void)updateViewportMetrics:(flutter::ViewportMetrics)viewportMetrics {
if (!self.platformView) {
return;
@ -412,16 +429,16 @@ static constexpr int kNumProfilerSamplesPerSec = 5;
self.iosPlatformView->SetOwnerViewController(_viewController);
[self maybeSetupPlatformViewChannels];
[self updateDisplays];
self.textInputPlugin.viewController = viewController;
_textInputPlugin.get().viewController = viewController;
if (viewController) {
__weak __block FlutterEngine* weakSelf = self;
__block FlutterEngine* blockSelf = self;
self.flutterViewControllerWillDeallocObserver =
[[NSNotificationCenter defaultCenter] addObserverForName:FlutterViewControllerWillDealloc
object:viewController
queue:[NSOperationQueue mainQueue]
usingBlock:^(NSNotification* note) {
[weakSelf notifyViewControllerDeallocated];
[blockSelf notifyViewControllerDeallocated];
}];
} else {
self.flutterViewControllerWillDeallocObserver = nil;
@ -438,15 +455,16 @@ static constexpr int kNumProfilerSamplesPerSec = 5;
if (_flutterViewControllerWillDeallocObserver) {
[[NSNotificationCenter defaultCenter]
removeObserver:_flutterViewControllerWillDeallocObserver];
[_flutterViewControllerWillDeallocObserver release];
}
_flutterViewControllerWillDeallocObserver = observer;
_flutterViewControllerWillDeallocObserver = [observer retain];
}
}
- (void)notifyViewControllerDeallocated {
[self.lifecycleChannel sendMessage:@"AppLifecycleState.detached"];
self.textInputPlugin.viewController = nil;
if (!self.allowHeadlessExecution) {
[[self lifecycleChannel] sendMessage:@"AppLifecycleState.detached"];
_textInputPlugin.get().viewController = nil;
if (!_allowHeadlessExecution) {
[self destroyContext];
} else if (_shell) {
flutter::PlatformViewIOS* platform_view = [self iosPlatformView];
@ -454,7 +472,7 @@ static constexpr int kNumProfilerSamplesPerSec = 5;
platform_view->SetOwnerViewController({});
}
}
[self.textInputPlugin resetViewResponder];
[_textInputPlugin.get() resetViewResponder];
_viewController.reset();
}
@ -474,46 +492,88 @@ static constexpr int kNumProfilerSamplesPerSec = 5;
return _viewController.get();
}
- (FlutterPlatformPlugin*)platformPlugin {
return _platformPlugin.get();
}
- (std::shared_ptr<flutter::PlatformViewsController>&)platformViewsController {
return _platformViewsController;
}
- (FlutterTextInputPlugin*)textInputPlugin {
return _textInputPlugin.get();
}
- (FlutterUndoManagerPlugin*)undoManagerPlugin {
return _undoManagerPlugin.get();
}
- (FlutterRestorationPlugin*)restorationPlugin {
return _restorationPlugin.get();
}
- (FlutterMethodChannel*)localizationChannel {
return _localizationChannel.get();
}
- (FlutterMethodChannel*)navigationChannel {
return _navigationChannel.get();
}
- (FlutterMethodChannel*)restorationChannel {
return _restorationChannel.get();
}
- (FlutterMethodChannel*)platformChannel {
return _platformChannel.get();
}
- (FlutterMethodChannel*)textInputChannel {
return _textInputChannel.get();
}
- (FlutterMethodChannel*)undoManagerChannel {
return _undoManagerChannel.get();
}
- (FlutterMethodChannel*)scribbleChannel {
return _scribbleChannel.get();
}
- (FlutterMethodChannel*)spellCheckChannel {
return _spellCheckChannel.get();
}
- (FlutterBasicMessageChannel*)lifecycleChannel {
return _lifecycleChannel.get();
}
- (FlutterBasicMessageChannel*)systemChannel {
return _systemChannel.get();
}
- (FlutterBasicMessageChannel*)settingsChannel {
return _settingsChannel.get();
}
- (FlutterBasicMessageChannel*)keyEventChannel {
return _keyEventChannel.get();
}
- (NSURL*)observatoryUrl {
return self.publisher.url;
return [_publisher.get() url];
}
- (NSURL*)vmServiceUrl {
return self.publisher.url;
return [_publisher.get() url];
}
- (void)resetChannels {
self.localizationChannel = nil;
self.navigationChannel = nil;
self.restorationChannel = nil;
self.platformChannel = nil;
self.platformViewsChannel = nil;
self.textInputChannel = nil;
self.undoManagerChannel = nil;
self.scribbleChannel = nil;
self.lifecycleChannel = nil;
self.systemChannel = nil;
self.settingsChannel = nil;
self.keyEventChannel = nil;
self.spellCheckChannel = nil;
_localizationChannel.reset();
_navigationChannel.reset();
_restorationChannel.reset();
_platformChannel.reset();
_platformViewsChannel.reset();
_textInputChannel.reset();
_undoManagerChannel.reset();
_scribbleChannel.reset();
_lifecycleChannel.reset();
_systemChannel.reset();
_settingsChannel.reset();
_keyEventChannel.reset();
_spellCheckChannel.reset();
}
- (void)startProfiler {
FML_DCHECK(!_threadHost->name_prefix.empty());
_profiler_metrics = std::make_shared<flutter::ProfilerMetricsIOS>();
__weak FlutterEngine* weakSelf = self;
_profiler = std::make_shared<flutter::SamplingProfiler>(
_threadHost->name_prefix.c_str(), _threadHost->profiler_thread->GetTaskRunner(),
[weakSelf]() {
FlutterEngine* strongSelf = weakSelf;
return strongSelf ? strongSelf->_profiler_metrics->GenerateSample()
: flutter::ProfileSample{};
},
kNumProfilerSamplesPerSec);
[self]() { return self->_profiler_metrics->GenerateSample(); }, kNumProfilerSamplesPerSec);
_profiler->Start();
}
@ -523,155 +583,165 @@ static constexpr int kNumProfilerSamplesPerSec = 5;
- (void)setUpChannels {
// This will be invoked once the shell is done setting up and the isolate ID
// for the UI isolate is available.
__weak FlutterEngine* weakSelf = self;
fml::WeakNSObject<FlutterEngine> weakSelf = [self getWeakNSObject];
[_binaryMessenger setMessageHandlerOnChannel:@"flutter/isolate"
binaryMessageHandler:^(NSData* message, FlutterBinaryReply reply) {
if (weakSelf) {
weakSelf.isolateId =
weakSelf.get().isolateId =
[[FlutterStringCodec sharedInstance] decode:message];
}
}];
self.localizationChannel =
[[FlutterMethodChannel alloc] initWithName:@"flutter/localization"
binaryMessenger:self.binaryMessenger
codec:[FlutterJSONMethodCodec sharedInstance]];
_localizationChannel.reset([[FlutterMethodChannel alloc]
initWithName:@"flutter/localization"
binaryMessenger:self.binaryMessenger
codec:[FlutterJSONMethodCodec sharedInstance]]);
self.navigationChannel =
[[FlutterMethodChannel alloc] initWithName:@"flutter/navigation"
binaryMessenger:self.binaryMessenger
codec:[FlutterJSONMethodCodec sharedInstance]];
_navigationChannel.reset([[FlutterMethodChannel alloc]
initWithName:@"flutter/navigation"
binaryMessenger:self.binaryMessenger
codec:[FlutterJSONMethodCodec sharedInstance]]);
if ([_initialRoute length] > 0) {
// Flutter isn't ready to receive this method call yet but the channel buffer will cache this.
[self.navigationChannel invokeMethod:@"setInitialRoute" arguments:_initialRoute];
[_navigationChannel invokeMethod:@"setInitialRoute" arguments:_initialRoute];
[_initialRoute release];
_initialRoute = nil;
}
self.restorationChannel =
[[FlutterMethodChannel alloc] initWithName:@"flutter/restoration"
binaryMessenger:self.binaryMessenger
codec:[FlutterStandardMethodCodec sharedInstance]];
_restorationChannel.reset([[FlutterMethodChannel alloc]
initWithName:@"flutter/restoration"
binaryMessenger:self.binaryMessenger
codec:[FlutterStandardMethodCodec sharedInstance]]);
self.platformChannel =
[[FlutterMethodChannel alloc] initWithName:@"flutter/platform"
binaryMessenger:self.binaryMessenger
codec:[FlutterJSONMethodCodec sharedInstance]];
_platformChannel.reset([[FlutterMethodChannel alloc]
initWithName:@"flutter/platform"
binaryMessenger:self.binaryMessenger
codec:[FlutterJSONMethodCodec sharedInstance]]);
self.platformViewsChannel =
[[FlutterMethodChannel alloc] initWithName:@"flutter/platform_views"
binaryMessenger:self.binaryMessenger
codec:[FlutterStandardMethodCodec sharedInstance]];
_platformViewsChannel.reset([[FlutterMethodChannel alloc]
initWithName:@"flutter/platform_views"
binaryMessenger:self.binaryMessenger
codec:[FlutterStandardMethodCodec sharedInstance]]);
self.textInputChannel =
[[FlutterMethodChannel alloc] initWithName:@"flutter/textinput"
binaryMessenger:self.binaryMessenger
codec:[FlutterJSONMethodCodec sharedInstance]];
_textInputChannel.reset([[FlutterMethodChannel alloc]
initWithName:@"flutter/textinput"
binaryMessenger:self.binaryMessenger
codec:[FlutterJSONMethodCodec sharedInstance]]);
self.undoManagerChannel =
[[FlutterMethodChannel alloc] initWithName:@"flutter/undomanager"
binaryMessenger:self.binaryMessenger
codec:[FlutterJSONMethodCodec sharedInstance]];
_undoManagerChannel.reset([[FlutterMethodChannel alloc]
initWithName:@"flutter/undomanager"
binaryMessenger:self.binaryMessenger
codec:[FlutterJSONMethodCodec sharedInstance]]);
self.scribbleChannel =
[[FlutterMethodChannel alloc] initWithName:@"flutter/scribble"
binaryMessenger:self.binaryMessenger
codec:[FlutterJSONMethodCodec sharedInstance]];
_scribbleChannel.reset([[FlutterMethodChannel alloc]
initWithName:@"flutter/scribble"
binaryMessenger:self.binaryMessenger
codec:[FlutterJSONMethodCodec sharedInstance]]);
self.spellCheckChannel =
[[FlutterMethodChannel alloc] initWithName:@"flutter/spellcheck"
binaryMessenger:self.binaryMessenger
codec:[FlutterStandardMethodCodec sharedInstance]];
_spellCheckChannel.reset([[FlutterMethodChannel alloc]
initWithName:@"flutter/spellcheck"
binaryMessenger:self.binaryMessenger
codec:[FlutterStandardMethodCodec sharedInstance]]);
self.lifecycleChannel =
[[FlutterBasicMessageChannel alloc] initWithName:@"flutter/lifecycle"
binaryMessenger:self.binaryMessenger
codec:[FlutterStringCodec sharedInstance]];
_lifecycleChannel.reset([[FlutterBasicMessageChannel alloc]
initWithName:@"flutter/lifecycle"
binaryMessenger:self.binaryMessenger
codec:[FlutterStringCodec sharedInstance]]);
self.systemChannel =
[[FlutterBasicMessageChannel alloc] initWithName:@"flutter/system"
binaryMessenger:self.binaryMessenger
codec:[FlutterJSONMessageCodec sharedInstance]];
_systemChannel.reset([[FlutterBasicMessageChannel alloc]
initWithName:@"flutter/system"
binaryMessenger:self.binaryMessenger
codec:[FlutterJSONMessageCodec sharedInstance]]);
self.settingsChannel =
[[FlutterBasicMessageChannel alloc] initWithName:@"flutter/settings"
binaryMessenger:self.binaryMessenger
codec:[FlutterJSONMessageCodec sharedInstance]];
_settingsChannel.reset([[FlutterBasicMessageChannel alloc]
initWithName:@"flutter/settings"
binaryMessenger:self.binaryMessenger
codec:[FlutterJSONMessageCodec sharedInstance]]);
self.keyEventChannel =
[[FlutterBasicMessageChannel alloc] initWithName:@"flutter/keyevent"
binaryMessenger:self.binaryMessenger
codec:[FlutterJSONMessageCodec sharedInstance]];
_keyEventChannel.reset([[FlutterBasicMessageChannel alloc]
initWithName:@"flutter/keyevent"
binaryMessenger:self.binaryMessenger
codec:[FlutterJSONMessageCodec sharedInstance]]);
self.textInputPlugin = [[FlutterTextInputPlugin alloc] initWithDelegate:self];
self.textInputPlugin.indirectScribbleDelegate = self;
[self.textInputPlugin setUpIndirectScribbleInteraction:self.viewController];
FlutterTextInputPlugin* textInputPlugin = [[FlutterTextInputPlugin alloc] initWithDelegate:self];
_textInputPlugin.reset(textInputPlugin);
textInputPlugin.indirectScribbleDelegate = self;
[textInputPlugin setUpIndirectScribbleInteraction:self.viewController];
self.undoManagerPlugin = [[FlutterUndoManagerPlugin alloc] initWithDelegate:self];
self.platformPlugin = [[FlutterPlatformPlugin alloc] initWithEngine:self];
FlutterUndoManagerPlugin* undoManagerPlugin =
[[FlutterUndoManagerPlugin alloc] initWithDelegate:self];
_undoManagerPlugin.reset(undoManagerPlugin);
self.restorationPlugin =
[[FlutterRestorationPlugin alloc] initWithChannel:self.restorationChannel
restorationEnabled:self.restorationEnabled];
self.spellCheckPlugin = [[FlutterSpellCheckPlugin alloc] init];
_platformPlugin.reset([[FlutterPlatformPlugin alloc] initWithEngine:[self getWeakNSObject]]);
self.screenshotChannel =
[[FlutterMethodChannel alloc] initWithName:@"flutter/screenshot"
binaryMessenger:self.binaryMessenger
codec:[FlutterStandardMethodCodec sharedInstance]];
_restorationPlugin.reset([[FlutterRestorationPlugin alloc]
initWithChannel:_restorationChannel.get()
restorationEnabled:_restorationEnabled]);
_spellCheckPlugin.reset([[FlutterSpellCheckPlugin alloc] init]);
[self.screenshotChannel setMethodCallHandler:^(FlutterMethodCall* _Nonnull call,
FlutterResult _Nonnull result) {
FlutterEngine* strongSelf = weakSelf;
if (!(strongSelf && strongSelf->_shell && strongSelf->_shell->IsSetup())) {
return result([FlutterError
errorWithCode:@"invalid_state"
message:@"Requesting screenshot while engine is not running."
details:nil]);
}
flutter::Rasterizer::Screenshot screenshot =
[strongSelf screenshot:flutter::Rasterizer::ScreenshotType::SurfaceData base64Encode:NO];
if (!screenshot.data) {
return result([FlutterError errorWithCode:@"failure"
message:@"Unable to get screenshot."
details:nil]);
}
// TODO(gaaclarke): Find way to eliminate this data copy.
NSData* data = [NSData dataWithBytes:screenshot.data->writable_data()
length:screenshot.data->size()];
NSString* format = [NSString stringWithUTF8String:screenshot.format.c_str()];
NSNumber* width = @(screenshot.frame_size.fWidth);
NSNumber* height = @(screenshot.frame_size.fHeight);
return result(@[ width, height, format ?: [NSNull null], data ]);
}];
_screenshotChannel.reset([[FlutterMethodChannel alloc]
initWithName:@"flutter/screenshot"
binaryMessenger:self.binaryMessenger
codec:[FlutterStandardMethodCodec sharedInstance]]);
[_screenshotChannel.get()
setMethodCallHandler:^(FlutterMethodCall* _Nonnull call, FlutterResult _Nonnull result) {
if (!(weakSelf.get() && weakSelf.get()->_shell && weakSelf.get()->_shell->IsSetup())) {
return result([FlutterError
errorWithCode:@"invalid_state"
message:@"Requesting screenshot while engine is not running."
details:nil]);
}
flutter::Rasterizer::Screenshot screenshot =
[weakSelf.get() screenshot:flutter::Rasterizer::ScreenshotType::SurfaceData
base64Encode:NO];
if (!screenshot.data) {
return result([FlutterError errorWithCode:@"failure"
message:@"Unable to get screenshot."
details:nil]);
}
// TODO(gaaclarke): Find way to eliminate this data copy.
NSData* data = [NSData dataWithBytes:screenshot.data->writable_data()
length:screenshot.data->size()];
NSString* format = [NSString stringWithUTF8String:screenshot.format.c_str()];
NSNumber* width = @(screenshot.frame_size.fWidth);
NSNumber* height = @(screenshot.frame_size.fHeight);
return result(@[ width, height, format ?: [NSNull null], data ]);
}];
}
- (void)maybeSetupPlatformViewChannels {
if (_shell && self.shell.IsSetup()) {
__weak FlutterEngine* weakSelf = self;
[self.platformChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
[weakSelf.platformPlugin handleMethodCall:call result:result];
FlutterPlatformPlugin* platformPlugin = _platformPlugin.get();
[_platformChannel.get() setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
[platformPlugin handleMethodCall:call result:result];
}];
[self.platformViewsChannel
fml::WeakNSObject<FlutterEngine> weakSelf = [self getWeakNSObject];
[_platformViewsChannel.get()
setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
if (weakSelf) {
weakSelf.platformViewsController->OnMethodCall(call, result);
weakSelf.get().platformViewsController->OnMethodCall(call, result);
}
}];
[self.textInputChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
[weakSelf.textInputPlugin handleMethodCall:call result:result];
FlutterTextInputPlugin* textInputPlugin = _textInputPlugin.get();
[_textInputChannel.get() setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
[textInputPlugin handleMethodCall:call result:result];
}];
[self.undoManagerChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
[weakSelf.undoManagerPlugin handleMethodCall:call result:result];
}];
FlutterUndoManagerPlugin* undoManagerPlugin = _undoManagerPlugin.get();
[_undoManagerChannel.get()
setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
[undoManagerPlugin handleMethodCall:call result:result];
}];
[self.spellCheckChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
[weakSelf.spellCheckPlugin handleMethodCall:call result:result];
}];
FlutterSpellCheckPlugin* spellCheckPlugin = _spellCheckPlugin.get();
[_spellCheckChannel.get()
setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
[spellCheckPlugin handleMethodCall:call result:result];
}];
}
}
@ -684,9 +754,9 @@ static constexpr int kNumProfilerSamplesPerSec = 5;
libraryURI:(NSString*)libraryOrNil
entrypointArgs:(NSArray<NSString*>*)entrypointArgs {
// Launch the Dart application with the inferred run configuration.
self.shell.RunEngine([self.dartProject runConfigurationForEntrypoint:entrypoint
libraryOrNil:libraryOrNil
entrypointArgs:entrypointArgs]);
self.shell.RunEngine([_dartProject.get() runConfigurationForEntrypoint:entrypoint
libraryOrNil:libraryOrNil
entrypointArgs:entrypointArgs]);
}
- (void)setUpShell:(std::unique_ptr<flutter::Shell>)shell
@ -695,8 +765,8 @@ static constexpr int kNumProfilerSamplesPerSec = 5;
[self setUpChannels];
[self onLocaleUpdated:nil];
[self updateDisplays];
self.publisher = [[FlutterDartVMServicePublisher alloc]
initWithEnableVMServicePublication:doesVMServicePublication];
_publisher.reset([[FlutterDartVMServicePublisher alloc]
initWithEnableVMServicePublication:doesVMServicePublication]);
[self maybeSetupPlatformViewChannels];
_shell->SetGpuAvailability(_isGpuDisabled ? flutter::GpuAvailability::kUnavailable
: flutter::GpuAvailability::kAvailable);
@ -775,7 +845,7 @@ static void SetEntryPoint(flutter::Settings* settings, NSString* entrypoint, NSS
self.initialRoute = initialRoute;
auto settings = [self.dartProject settings];
auto settings = [_dartProject.get() settings];
if (initialRoute != nil) {
self.initialRoute = initialRoute;
} else if (settings.route.empty() == false) {
@ -784,28 +854,24 @@ static void SetEntryPoint(flutter::Settings* settings, NSString* entrypoint, NSS
FlutterView.forceSoftwareRendering = settings.enable_software_rendering;
auto platformData = [self.dartProject defaultPlatformData];
auto platformData = [_dartProject.get() defaultPlatformData];
SetEntryPoint(&settings, entrypoint, libraryURI);
NSString* threadLabel = [FlutterEngine generateThreadLabel:self.labelPrefix];
NSString* threadLabel = [FlutterEngine generateThreadLabel:_labelPrefix];
_threadHost = std::make_shared<flutter::ThreadHost>();
*_threadHost = MakeThreadHost(threadLabel, settings);
__weak FlutterEngine* weakSelf = self;
// Lambda captures by pointers to ObjC objects are fine here because the
// create call is synchronous.
flutter::Shell::CreateCallback<flutter::PlatformView> on_create_platform_view =
[weakSelf](flutter::Shell& shell) {
FlutterEngine* strongSelf = weakSelf;
if (!strongSelf) {
return std::unique_ptr<flutter::PlatformViewIOS>();
}
[strongSelf recreatePlatformViewController];
strongSelf->_platformViewsController->SetTaskRunner(
[self](flutter::Shell& shell) {
[self recreatePlatformViewController];
self->_platformViewsController->SetTaskRunner(
shell.GetTaskRunners().GetPlatformTaskRunner());
return std::make_unique<flutter::PlatformViewIOS>(
shell, strongSelf->_renderingApi, strongSelf->_platformViewsController,
shell.GetTaskRunners(), shell.GetConcurrentWorkerTaskRunner(),
shell.GetIsGpuDisabledSyncSwitch());
shell, self->_renderingApi, self->_platformViewsController, shell.GetTaskRunners(),
shell.GetConcurrentWorkerTaskRunner(), shell.GetIsGpuDisabledSyncSwitch());
};
flutter::Shell::CreateCallback<flutter::Rasterizer> on_create_rasterizer =
@ -922,7 +988,7 @@ static void SetEntryPoint(flutter::Settings* settings, NSString* entrypoint, NSS
if (_shell) {
_shell->NotifyLowMemoryWarning();
}
[self.systemChannel sendMessage:@{@"type" : @"memoryPressure"}];
[_systemChannel sendMessage:@{@"type" : @"memoryPressure"}];
}
#pragma mark - Text input delegate
@ -930,23 +996,23 @@ static void SetEntryPoint(flutter::Settings* settings, NSString* entrypoint, NSS
- (void)flutterTextInputView:(FlutterTextInputView*)textInputView
updateEditingClient:(int)client
withState:(NSDictionary*)state {
[self.textInputChannel invokeMethod:@"TextInputClient.updateEditingState"
arguments:@[ @(client), state ]];
[_textInputChannel.get() invokeMethod:@"TextInputClient.updateEditingState"
arguments:@[ @(client), state ]];
}
- (void)flutterTextInputView:(FlutterTextInputView*)textInputView
updateEditingClient:(int)client
withState:(NSDictionary*)state
withTag:(NSString*)tag {
[self.textInputChannel invokeMethod:@"TextInputClient.updateEditingStateWithTag"
arguments:@[ @(client), @{tag : state} ]];
[_textInputChannel.get() invokeMethod:@"TextInputClient.updateEditingStateWithTag"
arguments:@[ @(client), @{tag : state} ]];
}
- (void)flutterTextInputView:(FlutterTextInputView*)textInputView
updateEditingClient:(int)client
withDelta:(NSDictionary*)delta {
[self.textInputChannel invokeMethod:@"TextInputClient.updateEditingStateWithDeltas"
arguments:@[ @(client), delta ]];
[_textInputChannel.get() invokeMethod:@"TextInputClient.updateEditingStateWithDeltas"
arguments:@[ @(client), delta ]];
}
- (void)flutterTextInputView:(FlutterTextInputView*)textInputView
@ -965,8 +1031,8 @@ static void SetEntryPoint(flutter::Settings* settings, NSString* entrypoint, NSS
stateString = @"FloatingCursorDragState.end";
break;
}
[self.textInputChannel invokeMethod:@"TextInputClient.updateFloatingCursor"
arguments:@[ @(client), stateString, position ]];
[_textInputChannel.get() invokeMethod:@"TextInputClient.updateFloatingCursor"
arguments:@[ @(client), stateString, position ]];
}
- (void)flutterTextInputView:(FlutterTextInputView*)textInputView
@ -1012,22 +1078,22 @@ static void SetEntryPoint(flutter::Settings* settings, NSString* entrypoint, NSS
actionString = @"TextInputAction.newline";
break;
}
[self.textInputChannel invokeMethod:@"TextInputClient.performAction"
arguments:@[ @(client), actionString ]];
[_textInputChannel.get() invokeMethod:@"TextInputClient.performAction"
arguments:@[ @(client), actionString ]];
}
- (void)flutterTextInputView:(FlutterTextInputView*)textInputView
showAutocorrectionPromptRectForStart:(NSUInteger)start
end:(NSUInteger)end
withClient:(int)client {
[self.textInputChannel invokeMethod:@"TextInputClient.showAutocorrectionPromptRect"
arguments:@[ @(client), @(start), @(end) ]];
[_textInputChannel.get() invokeMethod:@"TextInputClient.showAutocorrectionPromptRect"
arguments:@[ @(client), @(start), @(end) ]];
}
- (void)flutterTextInputView:(FlutterTextInputView*)textInputView
willDismissEditMenuWithTextInputClient:(int)client {
[self.platformChannel invokeMethod:@"ContextMenu.onDismissSystemContextMenu"
arguments:@[ @(client) ]];
[_platformChannel.get() invokeMethod:@"ContextMenu.onDismissSystemContextMenu"
arguments:@[ @(client) ]];
}
#pragma mark - FlutterViewEngineDelegate
@ -1036,7 +1102,7 @@ static void SetEntryPoint(flutter::Settings* settings, NSString* entrypoint, NSS
// TODO(justinmc): Switch from the TextInputClient to Scribble channel when
// the framework has finished transitioning to the Scribble channel.
// https://github.com/flutter/flutter/pull/115296
[self.textInputChannel invokeMethod:@"TextInputClient.showToolbar" arguments:@[ @(client) ]];
[_textInputChannel.get() invokeMethod:@"TextInputClient.showToolbar" arguments:@[ @(client) ]];
}
- (void)flutterTextInputPlugin:(FlutterTextInputPlugin*)textInputPlugin
@ -1046,7 +1112,7 @@ static void SetEntryPoint(flutter::Settings* settings, NSString* entrypoint, NSS
// TODO(justinmc): Switch from the TextInputClient to Scribble channel when
// the framework has finished transitioning to the Scribble channel.
// https://github.com/flutter/flutter/pull/115296
[self.textInputChannel
[_textInputChannel.get()
invokeMethod:@"TextInputClient.focusElement"
arguments:@[ elementIdentifier, @(referencePoint.x), @(referencePoint.y) ]
result:callback];
@ -1058,7 +1124,7 @@ static void SetEntryPoint(flutter::Settings* settings, NSString* entrypoint, NSS
// TODO(justinmc): Switch from the TextInputClient to Scribble channel when
// the framework has finished transitioning to the Scribble channel.
// https://github.com/flutter/flutter/pull/115296
[self.textInputChannel
[_textInputChannel.get()
invokeMethod:@"TextInputClient.requestElementsInRect"
arguments:@[ @(rect.origin.x), @(rect.origin.y), @(rect.size.width), @(rect.size.height) ]
result:callback];
@ -1068,14 +1134,15 @@ static void SetEntryPoint(flutter::Settings* settings, NSString* entrypoint, NSS
// TODO(justinmc): Switch from the TextInputClient to Scribble channel when
// the framework has finished transitioning to the Scribble channel.
// https://github.com/flutter/flutter/pull/115296
[self.textInputChannel invokeMethod:@"TextInputClient.scribbleInteractionBegan" arguments:nil];
[_textInputChannel.get() invokeMethod:@"TextInputClient.scribbleInteractionBegan" arguments:nil];
}
- (void)flutterTextInputViewScribbleInteractionFinished:(FlutterTextInputView*)textInputView {
// TODO(justinmc): Switch from the TextInputClient to Scribble channel when
// the framework has finished transitioning to the Scribble channel.
// https://github.com/flutter/flutter/pull/115296
[self.textInputChannel invokeMethod:@"TextInputClient.scribbleInteractionFinished" arguments:nil];
[_textInputChannel.get() invokeMethod:@"TextInputClient.scribbleInteractionFinished"
arguments:nil];
}
- (void)flutterTextInputView:(FlutterTextInputView*)textInputView
@ -1084,8 +1151,8 @@ static void SetEntryPoint(flutter::Settings* settings, NSString* entrypoint, NSS
// TODO(justinmc): Switch from the TextInputClient to Scribble channel when
// the framework has finished transitioning to the Scribble channel.
// https://github.com/flutter/flutter/pull/115296
[self.textInputChannel invokeMethod:@"TextInputClient.insertTextPlaceholder"
arguments:@[ @(client), @(size.width), @(size.height) ]];
[_textInputChannel.get() invokeMethod:@"TextInputClient.insertTextPlaceholder"
arguments:@[ @(client), @(size.width), @(size.height) ]];
}
- (void)flutterTextInputView:(FlutterTextInputView*)textInputView
@ -1093,8 +1160,8 @@ static void SetEntryPoint(flutter::Settings* settings, NSString* entrypoint, NSS
// TODO(justinmc): Switch from the TextInputClient to Scribble channel when
// the framework has finished transitioning to the Scribble channel.
// https://github.com/flutter/flutter/pull/115296
[self.textInputChannel invokeMethod:@"TextInputClient.removeTextPlaceholder"
arguments:@[ @(client) ]];
[_textInputChannel.get() invokeMethod:@"TextInputClient.removeTextPlaceholder"
arguments:@[ @(client) ]];
}
- (void)flutterTextInputView:(FlutterTextInputView*)textInputView
@ -1102,8 +1169,8 @@ static void SetEntryPoint(flutter::Settings* settings, NSString* entrypoint, NSS
// When flutter text input view resign first responder, send a message to
// framework to ensure the focus state is correct. This is useful when close
// keyboard from platform side.
[self.textInputChannel invokeMethod:@"TextInputClient.onConnectionClosed"
arguments:@[ @(client) ]];
[_textInputChannel.get() invokeMethod:@"TextInputClient.onConnectionClosed"
arguments:@[ @(client) ]];
// Platform view's first responder detection logic:
//
@ -1130,7 +1197,7 @@ static void SetEntryPoint(flutter::Settings* settings, NSString* entrypoint, NSS
return;
}
[self.platformViewsChannel invokeMethod:@"viewFocused" arguments:@(platform_view_id)];
[_platformViewsChannel.get() invokeMethod:@"viewFocused" arguments:@(platform_view_id)];
});
}
@ -1138,7 +1205,7 @@ static void SetEntryPoint(flutter::Settings* settings, NSString* entrypoint, NSS
- (void)handleUndoWithDirection:(FlutterUndoRedoDirection)direction {
NSString* action = (direction == FlutterUndoRedoDirectionUndo) ? @"undo" : @"redo";
[self.undoManagerChannel invokeMethod:@"UndoManagerClient.handleUndo" arguments:@[ action ]];
[_undoManagerChannel.get() invokeMethod:@"UndoManagerClient.handleUndo" arguments:@[ action ]];
}
- (UIView<UITextInput>*)activeTextInputView {
@ -1177,7 +1244,8 @@ static void SetEntryPoint(flutter::Settings* settings, NSString* entrypoint, NSS
// Discard the previous messenger and keep the new one.
if (binaryMessenger != _binaryMessenger) {
_binaryMessenger.parent = nil;
_binaryMessenger = binaryMessenger;
[_binaryMessenger release];
_binaryMessenger = [binaryMessenger retain];
}
}
@ -1249,7 +1317,7 @@ static void SetEntryPoint(flutter::Settings* settings, NSString* entrypoint, NSS
#pragma mark - FlutterTextureRegistry
- (int64_t)registerTexture:(NSObject<FlutterTexture>*)texture {
int64_t textureId = self.nextTextureId++;
int64_t textureId = _nextTextureId++;
self.iosPlatformView->RegisterExternalTexture(textureId, texture);
return textureId;
}
@ -1282,7 +1350,7 @@ static void SetEntryPoint(flutter::Settings* settings, NSString* entrypoint, NSS
FlutterEngineRegistrar* result = [[FlutterEngineRegistrar alloc] initWithPlugin:pluginKey
flutterEngine:self];
self.registrars[pluginKey] = result;
return result;
return [result autorelease];
}
- (BOOL)hasPlugin:(NSString*)pluginKey {
@ -1338,10 +1406,10 @@ static void SetEntryPoint(flutter::Settings* settings, NSString* entrypoint, NSS
- (void)onLocaleUpdated:(NSNotification*)notification {
// Get and pass the user's preferred locale list to dart:ui.
NSMutableArray<NSString*>* localeData = [[NSMutableArray alloc] init];
NSMutableArray<NSString*>* localeData = [[[NSMutableArray alloc] init] autorelease];
NSArray<NSString*>* preferredLocales = [NSLocale preferredLanguages];
for (NSString* localeID in preferredLocales) {
NSLocale* locale = [[NSLocale alloc] initWithLocaleIdentifier:localeID];
NSLocale* locale = [[[NSLocale alloc] initWithLocaleIdentifier:localeID] autorelease];
NSString* languageCode = [locale objectForKey:NSLocaleLanguageCode];
NSString* countryCode = [locale objectForKey:NSLocaleCountryCode];
NSString* scriptCode = [locale objectForKey:NSLocaleScriptCode];
@ -1363,33 +1431,12 @@ static void SetEntryPoint(flutter::Settings* settings, NSString* entrypoint, NSS
- (void)waitForFirstFrame:(NSTimeInterval)timeout
callback:(void (^_Nonnull)(BOOL didTimeout))callback {
dispatch_queue_t queue = dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0);
__weak FlutterEngine* weakSelf = self;
dispatch_async(queue, ^{
FlutterEngine* strongSelf = weakSelf;
if (!strongSelf) {
return;
}
fml::TimeDelta waitTime = fml::TimeDelta::FromMilliseconds(timeout * 1000);
BOOL didTimeout =
strongSelf.shell.WaitForFirstFrame(waitTime).code() == fml::StatusCode::kDeadlineExceeded;
self.shell.WaitForFirstFrame(waitTime).code() == fml::StatusCode::kDeadlineExceeded;
dispatch_async(dispatch_get_main_queue(), ^{
// Capture strongSelf to ensure that destruction does not occur on a background thread.
//
// The containing block, executed on a background thread, strongly captures self, then makes a
// blocking call to self.shell.WaitForFirstFrame(). If, during this time, all other instances
// of self are released, the containing block's reference would be the last one, resulting in
// `[FlutterEngine dealloc]` being called when it goes out of scope at the end of that block,
// on a background thread. FlutterEngine owns a reference to a PlatformViewsController, which
// owns a WeakPtrFactory whose destructor asserts that it be freed on the platform thread. To
// avoid this, we capture strongSelf in the current block, which is executed on the platform
// thread.
//
// strongSelf is never nil here since it's a strong reference that's verified non-nil above,
// but we use a conditional check to avoid and unused expression compiler warning.
if (strongSelf) {
callback(didTimeout);
}
callback(didTimeout);
});
});
}
@ -1399,13 +1446,13 @@ static void SetEntryPoint(flutter::Settings* settings, NSString* entrypoint, NSS
initialRoute:(/*nullable*/ NSString*)initialRoute
entrypointArgs:(/*nullable*/ NSArray<NSString*>*)entrypointArgs {
NSAssert(_shell, @"Spawning from an engine without a shell (possibly not run).");
FlutterEngine* result = [[FlutterEngine alloc] initWithName:self.labelPrefix
project:self.dartProject
allowHeadlessExecution:self.allowHeadlessExecution];
FlutterEngine* result = [[FlutterEngine alloc] initWithName:_labelPrefix
project:_dartProject.get()
allowHeadlessExecution:_allowHeadlessExecution];
flutter::RunConfiguration configuration =
[self.dartProject runConfigurationForEntrypoint:entrypoint
libraryOrNil:libraryURI
entrypointArgs:entrypointArgs];
[_dartProject.get() runConfigurationForEntrypoint:entrypoint
libraryOrNil:libraryURI
entrypointArgs:entrypointArgs];
fml::WeakPtr<flutter::PlatformView> platform_view = _shell->GetPlatformView();
FML_DCHECK(platform_view);
@ -1442,7 +1489,7 @@ static void SetEntryPoint(flutter::Settings* settings, NSString* entrypoint, NSS
result->_profiler_metrics = _profiler_metrics;
result->_isGpuDisabled = _isGpuDisabled;
[result setUpShell:std::move(shell) withVMServicePublication:NO];
return result;
return [result autorelease];
}
- (const flutter::ThreadHost&)threadHost {
@ -1450,7 +1497,7 @@ static void SetEntryPoint(flutter::Settings* settings, NSString* entrypoint, NSS
}
- (FlutterDartProject*)project {
return self.dartProject;
return _dartProject.get();
}
- (BOOL)isUsingImpeller {
@ -1471,6 +1518,11 @@ static void SetEntryPoint(flutter::Settings* settings, NSString* entrypoint, NSS
return self;
}
- (void)dealloc {
[_pluginKey release];
[super dealloc];
}
- (NSObject<FlutterBinaryMessenger>*)messenger {
return _flutterEngine.binaryMessenger;
}

View File

@ -12,7 +12,7 @@
@interface FlutterPlatformPlugin : NSObject
- (instancetype)init NS_UNAVAILABLE;
+ (instancetype)new NS_UNAVAILABLE;
- (instancetype)initWithEngine:(FlutterEngine*)engine NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithEngine:(fml::WeakNSObject<FlutterEngine>)engine NS_DESIGNATED_INITIALIZER;
- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result;
@end

View File

@ -82,7 +82,7 @@ static void SetStatusBarStyleForSharedApplication(UIStatusBarStyle style) {
@implementation FlutterPlatformPlugin
- (instancetype)initWithEngine:(FlutterEngine*)engine {
- (instancetype)initWithEngine:(fml::WeakNSObject<FlutterEngine>)engine {
FML_DCHECK(engine) << "engine must be set";
self = [super init];

View File

@ -37,12 +37,15 @@ FLUTTER_ASSERT_ARC
OCMStub([mockApplication sharedApplication]).andReturn(mockApplication);
FlutterEngine* engine = [[FlutterEngine alloc] initWithName:@"test" project:nil];
std::unique_ptr<fml::WeakNSObjectFactory<FlutterEngine>> _weakFactory =
std::make_unique<fml::WeakNSObjectFactory<FlutterEngine>>(engine);
[engine runWithEntrypoint:nil];
XCTestExpectation* invokeExpectation =
[self expectationWithDescription:@"Web search launched with escaped search term"];
FlutterPlatformPlugin* plugin = [[FlutterPlatformPlugin alloc] initWithEngine:engine];
FlutterPlatformPlugin* plugin =
[[FlutterPlatformPlugin alloc] initWithEngine:_weakFactory->GetWeakNSObject()];
FlutterPlatformPlugin* mockPlugin = OCMPartialMock(plugin);
FlutterMethodCall* methodCall = [FlutterMethodCall methodCallWithMethodName:@"SearchWeb.invoke"
@ -68,12 +71,15 @@ FLUTTER_ASSERT_ARC
OCMStub([mockApplication sharedApplication]).andReturn(mockApplication);
FlutterEngine* engine = [[FlutterEngine alloc] initWithName:@"test" project:nil];
std::unique_ptr<fml::WeakNSObjectFactory<FlutterEngine>> _weakFactory =
std::make_unique<fml::WeakNSObjectFactory<FlutterEngine>>(engine);
[engine runWithEntrypoint:nil];
XCTestExpectation* invokeExpectation =
[self expectationWithDescription:@"Web search launched with non escaped search term"];
FlutterPlatformPlugin* plugin = [[FlutterPlatformPlugin alloc] initWithEngine:engine];
FlutterPlatformPlugin* plugin =
[[FlutterPlatformPlugin alloc] initWithEngine:_weakFactory->GetWeakNSObject()];
FlutterPlatformPlugin* mockPlugin = OCMPartialMock(plugin);
FlutterMethodCall* methodCall = [FlutterMethodCall methodCallWithMethodName:@"SearchWeb.invoke"
@ -97,6 +103,8 @@ FLUTTER_ASSERT_ARC
- (void)testLookUpCallInitiated {
FlutterEngine* engine = [[FlutterEngine alloc] initWithName:@"test" project:nil];
[engine runWithEntrypoint:nil];
std::unique_ptr<fml::WeakNSObjectFactory<FlutterEngine>> _weakFactory =
std::make_unique<fml::WeakNSObjectFactory<FlutterEngine>>(engine);
XCTestExpectation* presentExpectation =
[self expectationWithDescription:@"Look Up view controller presented"];
@ -106,7 +114,8 @@ FLUTTER_ASSERT_ARC
bundle:nil];
FlutterViewController* mockEngineViewController = OCMPartialMock(engineViewController);
FlutterPlatformPlugin* plugin = [[FlutterPlatformPlugin alloc] initWithEngine:engine];
FlutterPlatformPlugin* plugin =
[[FlutterPlatformPlugin alloc] initWithEngine:_weakFactory->GetWeakNSObject()];
FlutterPlatformPlugin* mockPlugin = OCMPartialMock(plugin);
FlutterMethodCall* methodCall = [FlutterMethodCall methodCallWithMethodName:@"LookUp.invoke"
@ -125,6 +134,8 @@ FLUTTER_ASSERT_ARC
- (void)testShareScreenInvoked {
FlutterEngine* engine = [[FlutterEngine alloc] initWithName:@"test" project:nil];
[engine runWithEntrypoint:nil];
std::unique_ptr<fml::WeakNSObjectFactory<FlutterEngine>> _weakFactory =
std::make_unique<fml::WeakNSObjectFactory<FlutterEngine>>(engine);
XCTestExpectation* presentExpectation =
[self expectationWithDescription:@"Share view controller presented"];
@ -138,7 +149,8 @@ FLUTTER_ASSERT_ARC
animated:YES
completion:nil]);
FlutterPlatformPlugin* plugin = [[FlutterPlatformPlugin alloc] initWithEngine:engine];
FlutterPlatformPlugin* plugin =
[[FlutterPlatformPlugin alloc] initWithEngine:_weakFactory->GetWeakNSObject()];
FlutterPlatformPlugin* mockPlugin = OCMPartialMock(plugin);
FlutterMethodCall* methodCall = [FlutterMethodCall methodCallWithMethodName:@"Share.invoke"
@ -157,6 +169,8 @@ FLUTTER_ASSERT_ARC
- (void)testShareScreenInvokedOnIPad {
FlutterEngine* engine = [[FlutterEngine alloc] initWithName:@"test" project:nil];
[engine runWithEntrypoint:nil];
std::unique_ptr<fml::WeakNSObjectFactory<FlutterEngine>> _weakFactory =
std::make_unique<fml::WeakNSObjectFactory<FlutterEngine>>(engine);
XCTestExpectation* presentExpectation =
[self expectationWithDescription:@"Share view controller presented on iPad"];
@ -173,7 +187,8 @@ FLUTTER_ASSERT_ARC
id mockTraitCollection = OCMClassMock([UITraitCollection class]);
OCMStub([mockTraitCollection userInterfaceIdiom]).andReturn(UIUserInterfaceIdiomPad);
FlutterPlatformPlugin* plugin = [[FlutterPlatformPlugin alloc] initWithEngine:engine];
FlutterPlatformPlugin* plugin =
[[FlutterPlatformPlugin alloc] initWithEngine:_weakFactory->GetWeakNSObject()];
FlutterPlatformPlugin* mockPlugin = OCMPartialMock(plugin);
FlutterMethodCall* methodCall = [FlutterMethodCall methodCallWithMethodName:@"Share.invoke"
@ -192,7 +207,10 @@ FLUTTER_ASSERT_ARC
- (void)testClipboardHasCorrectStrings {
[UIPasteboard generalPasteboard].string = nil;
FlutterEngine* engine = [[FlutterEngine alloc] initWithName:@"test" project:nil];
FlutterPlatformPlugin* plugin = [[FlutterPlatformPlugin alloc] initWithEngine:engine];
std::unique_ptr<fml::WeakNSObjectFactory<FlutterEngine>> _weakFactory =
std::make_unique<fml::WeakNSObjectFactory<FlutterEngine>>(engine);
FlutterPlatformPlugin* plugin =
[[FlutterPlatformPlugin alloc] initWithEngine:_weakFactory->GetWeakNSObject()];
XCTestExpectation* setStringExpectation = [self expectationWithDescription:@"setString"];
FlutterResult resultSet = ^(id result) {
@ -228,7 +246,10 @@ FLUTTER_ASSERT_ARC
- (void)testClipboardSetDataToNullDoNotCrash {
[UIPasteboard generalPasteboard].string = nil;
FlutterEngine* engine = [[FlutterEngine alloc] initWithName:@"test" project:nil];
FlutterPlatformPlugin* plugin = [[FlutterPlatformPlugin alloc] initWithEngine:engine];
std::unique_ptr<fml::WeakNSObjectFactory<FlutterEngine>> _weakFactory =
std::make_unique<fml::WeakNSObjectFactory<FlutterEngine>>(engine);
FlutterPlatformPlugin* plugin =
[[FlutterPlatformPlugin alloc] initWithEngine:_weakFactory->GetWeakNSObject()];
XCTestExpectation* setStringExpectation = [self expectationWithDescription:@"setData"];
FlutterResult resultSet = ^(id result) {
@ -259,7 +280,10 @@ FLUTTER_ASSERT_ARC
[[UINavigationController alloc] initWithRootViewController:flutterViewController];
UITabBarController* tabBarController = [[UITabBarController alloc] init];
tabBarController.viewControllers = @[ navigationController ];
FlutterPlatformPlugin* plugin = [[FlutterPlatformPlugin alloc] initWithEngine:engine];
std::unique_ptr<fml::WeakNSObjectFactory<FlutterEngine>> _weakFactory =
std::make_unique<fml::WeakNSObjectFactory<FlutterEngine>>(engine);
FlutterPlatformPlugin* plugin =
[[FlutterPlatformPlugin alloc] initWithEngine:_weakFactory->GetWeakNSObject()];
id navigationControllerMock = OCMPartialMock(navigationController);
OCMStub([navigationControllerMock popViewControllerAnimated:YES]);
@ -279,9 +303,12 @@ FLUTTER_ASSERT_ARC
- (void)testWhetherDeviceHasLiveTextInputInvokeCorrectly {
FlutterEngine* engine = [[FlutterEngine alloc] initWithName:@"test" project:nil];
std::unique_ptr<fml::WeakNSObjectFactory<FlutterEngine>> _weakFactory =
std::make_unique<fml::WeakNSObjectFactory<FlutterEngine>>(engine);
XCTestExpectation* invokeExpectation =
[self expectationWithDescription:@"isLiveTextInputAvailableInvoke"];
FlutterPlatformPlugin* plugin = [[FlutterPlatformPlugin alloc] initWithEngine:engine];
FlutterPlatformPlugin* plugin =
[[FlutterPlatformPlugin alloc] initWithEngine:_weakFactory->GetWeakNSObject()];
FlutterPlatformPlugin* mockPlugin = OCMPartialMock(plugin);
FlutterMethodCall* methodCall =
[FlutterMethodCall methodCallWithMethodName:@"LiveText.isLiveTextInputAvailable"
@ -304,6 +331,8 @@ FLUTTER_ASSERT_ARC
[engine runWithEntrypoint:nil];
FlutterViewController* flutterViewController =
[[FlutterViewController alloc] initWithEngine:engine nibName:nil bundle:nil];
std::unique_ptr<fml::WeakNSObjectFactory<FlutterEngine>> _weakFactory =
std::make_unique<fml::WeakNSObjectFactory<FlutterEngine>>(engine);
XCTAssertFalse(flutterViewController.prefersStatusBarHidden);
// Update to hidden.
@ -342,6 +371,8 @@ FLUTTER_ASSERT_ARC
[engine runWithEntrypoint:nil];
FlutterViewController* flutterViewController =
[[FlutterViewController alloc] initWithEngine:engine nibName:nil bundle:nil];
std::unique_ptr<fml::WeakNSObjectFactory<FlutterEngine>> _weakFactory =
std::make_unique<fml::WeakNSObjectFactory<FlutterEngine>>(engine);
XCTAssertFalse(flutterViewController.prefersStatusBarHidden);
// Update to hidden.
@ -389,6 +420,8 @@ FLUTTER_ASSERT_ARC
[engine runWithEntrypoint:nil];
FlutterViewController* flutterViewController =
[[FlutterViewController alloc] initWithEngine:engine nibName:nil bundle:nil];
std::unique_ptr<fml::WeakNSObjectFactory<FlutterEngine>> _weakFactory =
std::make_unique<fml::WeakNSObjectFactory<FlutterEngine>>(engine);
// Update to hidden.
FlutterPlatformPlugin* plugin = [engine platformPlugin];
@ -438,6 +471,8 @@ FLUTTER_ASSERT_ARC
[engine runWithEntrypoint:nil];
FlutterViewController* flutterViewController =
[[FlutterViewController alloc] initWithEngine:engine nibName:nil bundle:nil];
std::unique_ptr<fml::WeakNSObjectFactory<FlutterEngine>> _weakFactory =
std::make_unique<fml::WeakNSObjectFactory<FlutterEngine>>(engine);
XCTAssertFalse(flutterViewController.prefersStatusBarHidden);
FlutterPlatformPlugin* plugin = [engine platformPlugin];

View File

@ -1146,7 +1146,6 @@ extern NSNotificationName const FlutterViewControllerWillDealloc;
nibName:nil
bundle:nil];
weakViewController = viewController;
[viewController loadView];
[viewController viewDidLoad];
weakView = viewController.view;
XCTAssertTrue([viewController.view isKindOfClass:[FlutterView class]]);