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

Commit 6fad27b

Browse files
authored
[macOS] Consolidate view management (#52254)
This PR improves view management logic of the macOS `FlutterEngine` class. * View operation assertions are now centralized in `registerViewController:` and `deregisterViewControllerForIdentifier:`. * `addViewController` now directly calls `.viewController =` on implicit views, so that it matches its verbatim description. * The doc for `addViewController` correctly reflects the fact that it doesn't support multiple views yet. Additionally, a useless (for now) member variable is removed. ## Pre-launch Checklist - [ ] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [ ] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [ ] I read and followed the [Flutter Style Guide] and the [C++, Objective-C, Java style guides]. - [ ] I listed at least one issue that this PR fixes in the description above. - [ ] 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. - [ ] I updated/added relevant documentation (doc comments with `///`). - [ ] I signed the [CLA]. - [ ] 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
1 parent 7141730 commit 6fad27b

File tree

2 files changed

+32
-35
lines changed

2 files changed

+32
-35
lines changed

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

Lines changed: 28 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -456,9 +456,6 @@ @implementation FlutterEngine {
456456

457457
FlutterThreadSynchronizer* _threadSynchronizer;
458458

459-
// The next available view ID.
460-
int _nextviewIdentifier;
461-
462459
// Whether the application is currently the active application.
463460
BOOL _active;
464461

@@ -515,8 +512,6 @@ - (instancetype)initWithName:(NSString*)labelPrefix
515512
_binaryMessenger = [[FlutterBinaryMessengerRelay alloc] initWithParent:self];
516513
_isResponseValid = [[NSMutableArray alloc] initWithCapacity:1];
517514
[_isResponseValid addObject:@YES];
518-
// kFlutterImplicitViewId is reserved for the implicit view.
519-
_nextviewIdentifier = kFlutterImplicitViewId + 1;
520515

521516
_embedderAPI.struct_size = sizeof(FlutterEngineProcTable);
522517
FlutterEngineGetProcAddresses(&_embedderAPI);
@@ -736,15 +731,25 @@ - (void)loadAOTData:(NSString*)assetsDir {
736731
- (void)registerViewController:(FlutterViewController*)controller
737732
forIdentifier:(FlutterViewIdentifier)viewIdentifier {
738733
NSAssert(controller != nil, @"The controller must not be nil.");
739-
NSAssert(![controller attached],
740-
@"The incoming view controller is already attached to an engine.");
734+
NSAssert(controller.engine == nil,
735+
@"The FlutterViewController is unexpectedly attached to "
736+
@"engine %@ before initialization.",
737+
controller.engine);
741738
NSAssert([_viewControllers objectForKey:@(viewIdentifier)] == nil,
742739
@"The requested view ID is occupied.");
740+
[_viewControllers setObject:controller forKey:@(viewIdentifier)];
743741
[controller setUpWithEngine:self
744742
viewIdentifier:viewIdentifier
745743
threadSynchronizer:_threadSynchronizer];
746744
NSAssert(controller.viewIdentifier == viewIdentifier, @"Failed to assign view ID.");
747-
[_viewControllers setObject:controller forKey:@(viewIdentifier)];
745+
// Verify that the controller's property are updated accordingly. Failing the
746+
// assertions is likely because either the FlutterViewController or the
747+
// FlutterEngine is mocked. Please subclass these classes instead.
748+
NSAssert(controller.attached, @"The FlutterViewController should switch to the attached mode "
749+
@"after it is added to a FlutterEngine.");
750+
NSAssert(controller.engine == self,
751+
@"The FlutterViewController was added to %@, but its engine unexpectedly became %@.",
752+
self, controller.engine);
748753

749754
if (controller.viewLoaded) {
750755
[self viewControllerViewDidLoad:controller];
@@ -779,11 +784,17 @@ - (void)viewControllerViewDidLoad:(FlutterViewController*)viewController {
779784
}
780785

781786
- (void)deregisterViewControllerForIdentifier:(FlutterViewIdentifier)viewIdentifier {
782-
FlutterViewController* oldController = [self viewControllerForIdentifier:viewIdentifier];
783-
if (oldController != nil) {
784-
[oldController detachFromEngine];
785-
[_viewControllers removeObjectForKey:@(viewIdentifier)];
787+
FlutterViewController* controller = [self viewControllerForIdentifier:viewIdentifier];
788+
// The controller can be nil. The engine stores only a weak ref, and this
789+
// method could have been called from the controller's dealloc.
790+
if (controller != nil) {
791+
[controller detachFromEngine];
792+
NSAssert(!controller.attached,
793+
@"The FlutterViewController unexpectedly stays attached after being removed. "
794+
@"In unit tests, this is likely because either the FlutterViewController or "
795+
@"the FlutterEngine is mocked. Please subclass these classes instead.");
786796
}
797+
[_viewControllers removeObjectForKey:@(viewIdentifier)];
787798
@synchronized(_vsyncWaiters) {
788799
[_vsyncWaiters removeObjectForKey:@(viewIdentifier)];
789800
}
@@ -877,26 +888,14 @@ - (FlutterCompositor*)createFlutterCompositor {
877888
#pragma mark - Framework-internal methods
878889

879890
- (void)addViewController:(FlutterViewController*)controller {
880-
NSAssert(controller.engine == nil,
881-
@"The FlutterViewController is unexpectedly attached to "
882-
@"engine %@ before initialization.",
883-
controller.engine);
884-
[self registerViewController:controller forIdentifier:kFlutterImplicitViewId];
885-
NSAssert(controller.attached,
886-
@"The FlutterViewController unexpectedly stays unattached after being added. "
887-
@"In unit tests, this is likely because either the FlutterViewController or "
888-
@"the FlutterEngine is mocked. Please subclass these classes instead.");
889-
NSAssert(controller.engine == self,
890-
@"The FlutterViewController #%lld has unexpected engine %@ after being added, "
891-
@"instead of %@. "
892-
@"In unit tests, this is likely because either the FlutterViewController or "
893-
@"the FlutterEngine is mocked. Please subclass these classes instead.",
894-
controller.viewIdentifier, controller.engine, self);
891+
// FlutterEngine can only handle the implicit view for now. Adding more views
892+
// throws an assertion.
893+
NSAssert(self.viewController == nil,
894+
@"The engine already has a view controller for the implicit view.");
895+
self.viewController = controller;
895896
}
896897

897898
- (void)removeViewController:(nonnull FlutterViewController*)viewController {
898-
NSAssert([viewController attached] && viewController.engine == self,
899-
@"The given view controller is not associated with this engine.");
900899
[self deregisterViewControllerForIdentifier:viewController.viewIdentifier];
901900
[self shutDownIfNeeded];
902901
}

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

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -125,9 +125,10 @@ typedef NS_ENUM(NSInteger, FlutterAppExitResponse) {
125125
/**
126126
* Attach a view controller to the engine as its default controller.
127127
*
128-
* Practically, since FlutterEngine can only be attached with one controller,
129-
* the given controller, if successfully attached, will always have the default
130-
* view ID kFlutterImplicitViewId.
128+
* Since FlutterEngine can only handle the implicit view for now, the given
129+
* controller will always be assigned to the implicit view, if there isn't an
130+
* implicit view yet. If the engine already has an implicit view, this call
131+
* throws an assertion.
131132
*
132133
* The engine holds a weak reference to the attached view controller.
133134
*
@@ -146,9 +147,6 @@ typedef NS_ENUM(NSInteger, FlutterAppExitResponse) {
146147
*
147148
* If the view controller is not associated with this engine, this call throws an
148149
* assertion.
149-
*
150-
* Practically, since FlutterEngine can only be attached with one controller for
151-
* now, the given controller must be the current view controller.
152150
*/
153151
- (void)removeViewController:(FlutterViewController*)viewController;
154152

0 commit comments

Comments
 (0)