Fix iOS platform view not deallocated (#18164)
Co-authored-by: Aaron Clarke <aaclarke@google.com>
diff --git a/shell/platform/darwin/ios/framework/Source/SemanticsObject.mm b/shell/platform/darwin/ios/framework/Source/SemanticsObject.mm
index 229e398..0818971 100644
--- a/shell/platform/darwin/ios/framework/Source/SemanticsObject.mm
+++ b/shell/platform/darwin/ios/framework/Source/SemanticsObject.mm
@@ -508,10 +508,12 @@
@end
-@implementation FlutterPlatformViewSemanticsContainer {
- SemanticsObject* _semanticsObject;
- UIView* _platformView;
-}
+@interface FlutterPlatformViewSemanticsContainer ()
+@property(nonatomic, assign) SemanticsObject* semanticsObject;
+@property(nonatomic, strong) UIView* platformView;
+@end
+
+@implementation FlutterPlatformViewSemanticsContainer
// Method declared as unavailable in the interface
- (instancetype)init {
@@ -531,13 +533,22 @@
flutter::FlutterPlatformViewsController* controller =
object.bridge->GetPlatformViewsController();
if (controller) {
- _platformView = [controller->GetPlatformViewByID(object.node.platformViewId) view];
+ _platformView = [[controller->GetPlatformViewByID(object.node.platformViewId) view] retain];
}
- self.accessibilityElements = @[ _semanticsObject, _platformView ];
}
return self;
}
+- (void)dealloc {
+ [_platformView release];
+ _platformView = nil;
+ [super dealloc];
+}
+
+- (NSArray*)accessibilityElements {
+ return @[ _semanticsObject, _platformView ];
+}
+
- (CGRect)accessibilityFrame {
return _semanticsObject.accessibilityFrame;
}
diff --git a/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm b/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm
index bc13dfb..08d8069 100644
--- a/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm
+++ b/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm
@@ -89,11 +89,11 @@
if (object.node.IsPlatformViewNode()) {
FlutterPlatformViewsController* controller = GetPlatformViewsController();
if (controller) {
- object.platformViewSemanticsContainer =
- [[FlutterPlatformViewSemanticsContainer alloc] initWithSemanticsObject:object];
+ object.platformViewSemanticsContainer = [[[FlutterPlatformViewSemanticsContainer alloc]
+ initWithSemanticsObject:object] autorelease];
}
} else if (object.platformViewSemanticsContainer) {
- [object.platformViewSemanticsContainer release];
+ object.platformViewSemanticsContainer = nil;
}
}
diff --git a/shell/platform/darwin/ios/framework/Source/accessibility_bridge_test.mm b/shell/platform/darwin/ios/framework/Source/accessibility_bridge_test.mm
index 6f17747..c242c43 100644
--- a/shell/platform/darwin/ios/framework/Source/accessibility_bridge_test.mm
+++ b/shell/platform/darwin/ios/framework/Source/accessibility_bridge_test.mm
@@ -5,11 +5,67 @@
#import <XCTest/XCTest.h>
#import "flutter/shell/platform/darwin/common/framework/Headers/FlutterMacros.h"
+#import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterPlatformViews.h"
+#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h"
#import "flutter/shell/platform/darwin/ios/framework/Source/accessibility_bridge.h"
#import "flutter/shell/platform/darwin/ios/platform_view_ios.h"
#import "third_party/ocmock/Source/OCMock/OCMock.h"
FLUTTER_ASSERT_NOT_ARC
+@class MockPlatformView;
+static MockPlatformView* gMockPlatformView = nil;
+
+@interface MockPlatformView : UIView
+@end
+@implementation MockPlatformView
+
+- (instancetype)init {
+ self = [super init];
+ if (self) {
+ gMockPlatformView = self;
+ }
+ return self;
+}
+
+- (void)dealloc {
+ gMockPlatformView = nil;
+ [super dealloc];
+}
+
+@end
+
+@interface MockFlutterPlatformView : NSObject <FlutterPlatformView>
+@property(nonatomic, strong) UIView* view;
+@end
+
+@implementation MockFlutterPlatformView
+
+- (instancetype)init {
+ if (self = [super init]) {
+ _view = [[MockPlatformView alloc] init];
+ }
+ return self;
+}
+
+- (void)dealloc {
+ [_view release];
+ _view = nil;
+ [super dealloc];
+}
+
+@end
+
+@interface MockFlutterPlatformFactory : NSObject <FlutterPlatformViewFactory>
+@end
+
+@implementation MockFlutterPlatformFactory
+- (NSObject<FlutterPlatformView>*)createWithFrame:(CGRect)frame
+ viewIdentifier:(int64_t)viewId
+ arguments:(id _Nullable)args {
+ return [[[MockFlutterPlatformView alloc] init] autorelease];
+}
+
+@end
namespace flutter {
namespace {
@@ -131,4 +187,55 @@
OCMVerifyAll(mockFlutterView);
}
+- (void)testSemanticsDeallocated {
+ @autoreleasepool {
+ flutter::MockDelegate mock_delegate;
+ auto thread_task_runner = CreateNewThread("AccessibilityBridgeTest");
+ flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
+ /*platform=*/thread_task_runner,
+ /*raster=*/thread_task_runner,
+ /*ui=*/thread_task_runner,
+ /*io=*/thread_task_runner);
+ auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
+ /*delegate=*/mock_delegate,
+ /*rendering_api=*/flutter::IOSRenderingAPI::kSoftware,
+ /*task_runners=*/runners);
+ id mockFlutterView = OCMClassMock([FlutterView class]);
+ std::string label = "some label";
+
+ auto flutterPlatformViewsController =
+ std::make_unique<flutter::FlutterPlatformViewsController>();
+ flutterPlatformViewsController->SetFlutterView(mockFlutterView);
+
+ MockFlutterPlatformFactory* factory = [[MockFlutterPlatformFactory new] autorelease];
+ flutterPlatformViewsController->RegisterViewFactory(
+ factory, @"MockFlutterPlatformView",
+ FlutterPlatformViewGestureRecognizersBlockingPolicyEager);
+ FlutterResult result = ^(id result) {
+ };
+ flutterPlatformViewsController->OnMethodCall(
+ [FlutterMethodCall
+ methodCallWithMethodName:@"create"
+ arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
+ result);
+
+ auto bridge = std::make_unique<flutter::AccessibilityBridge>(
+ /*view=*/mockFlutterView,
+ /*platform_view=*/platform_view.get(),
+ /*platform_views_controller=*/flutterPlatformViewsController.get());
+
+ flutter::SemanticsNodeUpdates nodes;
+ flutter::SemanticsNode semantics_node;
+ semantics_node.id = 2;
+ semantics_node.platformViewId = 2;
+ semantics_node.label = label;
+ nodes[kRootNodeId] = semantics_node;
+ flutter::CustomAccessibilityActionUpdates actions;
+ bridge->UpdateSemantics(/*nodes=*/nodes, /*actions=*/actions);
+ XCTAssertNotNil(gMockPlatformView);
+ flutterPlatformViewsController->Reset();
+ }
+ XCTAssertNil(gMockPlatformView);
+}
+
@end