From f553caaa220bef82fce31ef9c25dbee0deb5cd6e Mon Sep 17 00:00:00 2001 From: Jenn Magder Date: Fri, 10 May 2024 12:07:30 -0700 Subject: [PATCH] Migrate FlutterView, FlutterPlatformViews, FlutterOverlayView to ARC (#52535) Smart pointers support ARC as of https://github.com/flutter/engine/pull/47612, and the unit tests were migrated in https://github.com/flutter/engine/pull/48162. Migrate `FlutterView`, `FlutterPlatformViews`, and `FlutterOverlayView` from MRC to ARC. Part of https://github.com/flutter/flutter/issues/137801. --- shell/platform/darwin/ios/BUILD.gn | 15 +-- .../framework/Source/FlutterOverlayView.mm | 2 + .../framework/Source/FlutterPlatformViews.mm | 110 ++++++++---------- .../Source/FlutterPlatformViews_Internal.h | 35 +++--- .../Source/FlutterPlatformViews_Internal.mm | 50 +++----- .../ios/framework/Source/FlutterView.mm | 12 +- .../Source/FlutterViewControllerTest.mm | 12 +- .../IosUnitTests.xcodeproj/project.pbxproj | 2 + 8 files changed, 115 insertions(+), 123 deletions(-) diff --git a/shell/platform/darwin/ios/BUILD.gn b/shell/platform/darwin/ios/BUILD.gn index 2ddfd93ab10fb..e6b6c9f3d527e 100644 --- a/shell/platform/darwin/ios/BUILD.gn +++ b/shell/platform/darwin/ios/BUILD.gn @@ -73,6 +73,11 @@ source_set("flutter_framework_source_arc") { "framework/Source/FlutterKeyboardManager.mm", "framework/Source/FlutterMetalLayer.h", "framework/Source/FlutterMetalLayer.mm", + "framework/Source/FlutterOverlayView.h", + "framework/Source/FlutterOverlayView.mm", + "framework/Source/FlutterPlatformViews.mm", + "framework/Source/FlutterPlatformViews_Internal.h", + "framework/Source/FlutterPlatformViews_Internal.mm", "framework/Source/FlutterRestorationPlugin.h", "framework/Source/FlutterRestorationPlugin.mm", "framework/Source/FlutterSpellCheckPlugin.h", @@ -87,6 +92,9 @@ source_set("flutter_framework_source_arc") { "framework/Source/FlutterUndoManagerDelegate.h", "framework/Source/FlutterUndoManagerPlugin.h", "framework/Source/FlutterUndoManagerPlugin.mm", + "framework/Source/FlutterView.h", + "framework/Source/FlutterView.mm", + "framework/Source/FlutterViewResponder.h", "framework/Source/KeyCodeMap.g.mm", "framework/Source/KeyCodeMap_Internal.h", "framework/Source/UIViewController+FlutterScreenAndSceneIfLoaded.h", @@ -160,18 +168,11 @@ source_set("flutter_framework_source") { "framework/Source/FlutterEngineGroup.mm", "framework/Source/FlutterEngine_Internal.h", "framework/Source/FlutterHeadlessDartRunner.mm", - "framework/Source/FlutterOverlayView.h", - "framework/Source/FlutterOverlayView.mm", "framework/Source/FlutterPlatformPlugin.h", "framework/Source/FlutterPlatformPlugin.mm", - "framework/Source/FlutterPlatformViews.mm", - "framework/Source/FlutterPlatformViews_Internal.h", - "framework/Source/FlutterPlatformViews_Internal.mm", "framework/Source/FlutterPluginAppLifeCycleDelegate.mm", "framework/Source/FlutterSemanticsScrollView.h", "framework/Source/FlutterSemanticsScrollView.mm", - "framework/Source/FlutterView.h", - "framework/Source/FlutterView.mm", "framework/Source/FlutterViewController.mm", "framework/Source/FlutterViewController_Internal.h", "framework/Source/SemanticsObject.h", diff --git a/shell/platform/darwin/ios/framework/Source/FlutterOverlayView.mm b/shell/platform/darwin/ios/framework/Source/FlutterOverlayView.mm index dc29cd85dd7bf..2e98396d603c0 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterOverlayView.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterOverlayView.mm @@ -9,6 +9,8 @@ #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterView.h" #include "fml/platform/darwin/cf_utils.h" +FLUTTER_ASSERT_ARC + // This is mostly a duplication of FlutterView. // TODO(amirh): once GL support is in evaluate if we can merge this with FlutterView. @implementation FlutterOverlayView { diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index 41295f26a720d..46aafd83e5efe 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -11,6 +11,8 @@ #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterView.h" #import "flutter/shell/platform/darwin/ios/ios_surface.h" +FLUTTER_ASSERT_ARC + @implementation UIView (FirstResponder) - (BOOL)flt_hasFirstResponderInViewHierarchySubtree { if (self.isFirstResponder) { @@ -91,7 +93,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, overlay_view.reset([[FlutterOverlayView alloc] init]); overlay_view_wrapper.reset([[FlutterOverlayView alloc] init]); - auto ca_layer = fml::scoped_nsobject{[[overlay_view.get() layer] retain]}; + auto ca_layer = fml::scoped_nsobject{[overlay_view.get() layer]}; std::unique_ptr ios_surface = IOSSurface::Create(ios_context, ca_layer); std::unique_ptr surface = ios_surface->CreateGPUSurface(); @@ -105,7 +107,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, overlay_view_wrapper.reset([[FlutterOverlayView alloc] initWithContentsScale:screenScale pixelFormat:pixel_format]); - auto ca_layer = fml::scoped_nsobject{[[overlay_view.get() layer] retain]}; + auto ca_layer = fml::scoped_nsobject{[overlay_view.get() layer]}; std::unique_ptr ios_surface = IOSSurface::Create(ios_context, ca_layer); std::unique_ptr surface = ios_surface->CreateGPUSurface(gr_context); @@ -159,12 +161,12 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, } void FlutterPlatformViewsController::SetFlutterView(UIView* flutter_view) { - flutter_view_.reset([flutter_view retain]); + flutter_view_.reset(flutter_view); } void FlutterPlatformViewsController::SetFlutterViewController( UIViewController* flutter_view_controller) { - flutter_view_controller_.reset([flutter_view_controller retain]); + flutter_view_controller_.reset(flutter_view_controller); } UIViewController* FlutterPlatformViewsController::getFlutterViewController() { @@ -231,21 +233,19 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, // Set a unique view identifier, so the platform view can be identified in unit tests. platform_view.accessibilityIdentifier = [NSString stringWithFormat:@"platform_view[%lld]", viewId]; - views_[viewId] = fml::scoped_nsobject>([embedded_view retain]); + views_[viewId] = fml::scoped_nsobject>(embedded_view); - FlutterTouchInterceptingView* touch_interceptor = [[[FlutterTouchInterceptingView alloc] + FlutterTouchInterceptingView* touch_interceptor = [[FlutterTouchInterceptingView alloc] initWithEmbeddedView:platform_view platformViewsController:GetWeakPtr() - gestureRecognizersBlockingPolicy:gesture_recognizers_blocking_policies_[viewType]] - autorelease]; + gestureRecognizersBlockingPolicy:gesture_recognizers_blocking_policies_[viewType]]; touch_interceptors_[viewId] = - fml::scoped_nsobject([touch_interceptor retain]); + fml::scoped_nsobject(touch_interceptor); - ChildClippingView* clipping_view = - [[[ChildClippingView alloc] initWithFrame:CGRectZero] autorelease]; + ChildClippingView* clipping_view = [[ChildClippingView alloc] initWithFrame:CGRectZero]; [clipping_view addSubview:touch_interceptor]; - root_views_[viewId] = fml::scoped_nsobject([clipping_view retain]); + root_views_[viewId] = fml::scoped_nsobject(clipping_view); result(nil); } @@ -307,8 +307,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, FlutterPlatformViewGestureRecognizersBlockingPolicy gestureRecognizerBlockingPolicy) { std::string idString([factoryId UTF8String]); FML_CHECK(factories_.count(idString) == 0); - factories_[idString] = - fml::scoped_nsobject>([factory retain]); + factories_[idString] = fml::scoped_nsobject>(factory); gesture_recognizers_blocking_policies_[idString] = gestureRecognizerBlockingPolicy; } @@ -417,7 +416,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, long FlutterPlatformViewsController::FindFirstResponderPlatformViewId() { for (auto const& [id, root_view] : root_views_) { - if ((UIView*)(root_view.get()).flt_hasFirstResponderInViewHierarchySubtree) { + if (((UIView*)root_view.get()).flt_hasFirstResponderInViewHierarchySubtree) { return id; } } @@ -460,7 +459,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, ChildClippingView* clipView = (ChildClippingView*)embedded_view.superview; SkMatrix transformMatrix; - NSMutableArray* blurFilters = [[[NSMutableArray alloc] init] autorelease]; + NSMutableArray* blurFilters = [[NSMutableArray alloc] init]; FML_DCHECK(!clipView.maskView || [clipView.maskView isKindOfClass:[FlutterClippingMaskView class]]); if (clipView.maskView) { @@ -530,12 +529,11 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, // is not supported in Quartz's gaussianBlur CAFilter, so it is not used // to blur the PlatformView. CGFloat blurRadius = (*iter)->GetFilterMutation().GetFilter().asBlur()->sigma_x(); - UIVisualEffectView* visualEffectView = [[[UIVisualEffectView alloc] - initWithEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleLight]] autorelease]; - PlatformViewFilter* filter = - [[[PlatformViewFilter alloc] initWithFrame:frameInClipView - blurRadius:blurRadius - visualEffectView:visualEffectView] autorelease]; + UIVisualEffectView* visualEffectView = [[UIVisualEffectView alloc] + initWithEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleLight]]; + PlatformViewFilter* filter = [[PlatformViewFilter alloc] initWithFrame:frameInClipView + blurRadius:blurRadius + visualEffectView:visualEffectView]; if (!filter) { canApplyBlurBackdrop = NO; } else { @@ -950,11 +948,13 @@ @interface DelayingGestureRecognizer : UIGestureRecognizer )platformViewsController; @end -@implementation FlutterTouchInterceptingView { - fml::scoped_nsobject _delayingRecognizer; - FlutterPlatformViewGestureRecognizersBlockingPolicy _blockingPolicy; - UIView* _embeddedView; -} +@interface FlutterTouchInterceptingView () +@property(nonatomic, weak, readonly) UIView* embeddedView; +@property(nonatomic, readonly) DelayingGestureRecognizer* delayingRecognizer; +@property(nonatomic, readonly) FlutterPlatformViewGestureRecognizersBlockingPolicy blockingPolicy; +@end + +@implementation FlutterTouchInterceptingView - (instancetype)initWithEmbeddedView:(UIView*)embeddedView platformViewsController: (fml::WeakPtr)platformViewsController @@ -996,47 +998,42 @@ - (instancetype)initWithEmbeddedView:(UIView*)embeddedView [self addSubview:embeddedView]; - ForwardingGestureRecognizer* forwardingRecognizer = [[[ForwardingGestureRecognizer alloc] - initWithTarget:self - platformViewsController:std::move(platformViewsController)] autorelease]; + ForwardingGestureRecognizer* forwardingRecognizer = + [[ForwardingGestureRecognizer alloc] initWithTarget:self + platformViewsController:platformViewsController]; - _delayingRecognizer.reset([[DelayingGestureRecognizer alloc] - initWithTarget:self - action:nil - forwardingRecognizer:forwardingRecognizer]); + _delayingRecognizer = [[DelayingGestureRecognizer alloc] initWithTarget:self + action:nil + forwardingRecognizer:forwardingRecognizer]; _blockingPolicy = blockingPolicy; - [self addGestureRecognizer:_delayingRecognizer.get()]; + [self addGestureRecognizer:_delayingRecognizer]; [self addGestureRecognizer:forwardingRecognizer]; } return self; } -- (UIView*)embeddedView { - return [[_embeddedView retain] autorelease]; -} - - (void)releaseGesture { - _delayingRecognizer.get().state = UIGestureRecognizerStateFailed; + self.delayingRecognizer.state = UIGestureRecognizerStateFailed; } - (void)blockGesture { switch (_blockingPolicy) { case FlutterPlatformViewGestureRecognizersBlockingPolicyEager: // We block all other gesture recognizers immediately in this policy. - _delayingRecognizer.get().state = UIGestureRecognizerStateEnded; + self.delayingRecognizer.state = UIGestureRecognizerStateEnded; break; case FlutterPlatformViewGestureRecognizersBlockingPolicyWaitUntilTouchesEnded: - if (_delayingRecognizer.get().touchedEndedWithoutBlocking) { + if (self.delayingRecognizer.touchedEndedWithoutBlocking) { // If touchesEnded of the `DelayingGesureRecognizer` has been already invoked, // we want to set the state of the `DelayingGesureRecognizer` to // `UIGestureRecognizerStateEnded` as soon as possible. - _delayingRecognizer.get().state = UIGestureRecognizerStateEnded; + self.delayingRecognizer.state = UIGestureRecognizerStateEnded; } else { // If touchesEnded of the `DelayingGesureRecognizer` has not been invoked, // We will set a flag to notify the `DelayingGesureRecognizer` to set the state to // `UIGestureRecognizerStateEnded` when touchesEnded is called. - _delayingRecognizer.get().shouldEndInNextTouchesEnded = YES; + self.delayingRecognizer.shouldEndInNextTouchesEnded = YES; } break; default: @@ -1060,19 +1057,12 @@ - (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event { } - (id)accessibilityContainer { - return _flutterAccessibilityContainer; -} - -- (void)dealloc { - [_flutterAccessibilityContainer release]; - [super dealloc]; + return self.flutterAccessibilityContainer; } @end -@implementation DelayingGestureRecognizer { - fml::scoped_nsobject _forwardingRecognizer; -} +@implementation DelayingGestureRecognizer - (instancetype)initWithTarget:(id)target action:(SEL)action @@ -1082,9 +1072,9 @@ - (instancetype)initWithTarget:(id)target self.delaysTouchesBegan = YES; self.delaysTouchesEnded = YES; self.delegate = self; - self.shouldEndInNextTouchesEnded = NO; - self.touchedEndedWithoutBlocking = NO; - _forwardingRecognizer.reset([forwardingRecognizer retain]); + _shouldEndInNextTouchesEnded = NO; + _touchedEndedWithoutBlocking = NO; + _forwardingRecognizer = forwardingRecognizer; } return self; } @@ -1093,7 +1083,7 @@ - (BOOL)gestureRecognizer:(UIGestureRecognizer*)gestureRecognizer shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer*)otherGestureRecognizer { // The forwarding gesture recognizer should always get all touch events, so it should not be // required to fail by any other gesture recognizer. - return otherGestureRecognizer != _forwardingRecognizer.get() && otherGestureRecognizer != self; + return otherGestureRecognizer != _forwardingRecognizer && otherGestureRecognizer != self; } - (BOOL)gestureRecognizer:(UIGestureRecognizer*)gestureRecognizer @@ -1158,7 +1148,7 @@ - (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event { // At the start of each gesture sequence, we reset the `_flutterViewController`, // so that all the touch events in the same sequence are forwarded to the same // `_flutterViewController`. - _flutterViewController.reset([_platformViewsController->getFlutterViewController() retain]); + _flutterViewController.reset(_platformViewsController->getFlutterViewController()); } [_flutterViewController.get() touchesBegan:touches withEvent:event]; _currentTouchPointersCount += touches.count; diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h index c2357c3dac20d..6373e1feaa40c 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h @@ -81,18 +81,18 @@ // Determines the rect of the blur effect in the coordinate system of `backdropFilterView`'s // parentView. -@property(assign, nonatomic, readonly) CGRect frame; +@property(nonatomic, readonly) CGRect frame; // Determines the blur intensity. // // It is set as the value of `inputRadius` of the `gaussianFilter` that is internally used. -@property(assign, nonatomic, readonly) CGFloat blurRadius; +@property(nonatomic, readonly) CGFloat blurRadius; // This is the view to use to blur the PlatformView. // // It is a modified version of UIKit's `UIVisualEffectView`. // The inputRadius can be customized and it doesn't add any color saturation to the blurred view. -@property(nonatomic, retain, readonly) UIVisualEffectView* backdropFilterView; +@property(nonatomic, readonly) UIVisualEffectView* backdropFilterView; // For testing only. + (void)resetPreparation; @@ -210,16 +210,19 @@ class FlutterPlatformViewsController { fml::WeakPtr GetWeakPtr(); - void SetFlutterView(UIView* flutter_view); + void SetFlutterView(UIView* flutter_view) __attribute__((cf_audited_transfer)); - void SetFlutterViewController(UIViewController* flutter_view_controller); + void SetFlutterViewController(UIViewController* flutter_view_controller) + __attribute__((cf_audited_transfer)); - UIViewController* getFlutterViewController(); + UIViewController* getFlutterViewController() + __attribute__((cf_audited_transfer)); void RegisterViewFactory( NSObject* factory, NSString* factoryId, - FlutterPlatformViewGestureRecognizersBlockingPolicy gestureRecognizerBlockingPolicy); + FlutterPlatformViewGestureRecognizersBlockingPolicy gestureRecognizerBlockingPolicy) + __attribute__((cf_audited_transfer)); // Called at the beginning of each frame. void BeginFrame(SkISize frame_size); @@ -266,7 +269,8 @@ class FlutterPlatformViewsController { const std::shared_ptr& ios_context, std::unique_ptr frame); - void OnMethodCall(FlutterMethodCall* call, FlutterResult result); + void OnMethodCall(FlutterMethodCall* call, FlutterResult result) + __attribute__((cf_audited_transfer)); // Returns the platform view id if the platform view (or any of its descendant view) is the first // responder. Returns -1 if no such platform view is found. @@ -284,10 +288,13 @@ class FlutterPlatformViewsController { using LayersMap = std::map>>; - void OnCreate(FlutterMethodCall* call, FlutterResult result); - void OnDispose(FlutterMethodCall* call, FlutterResult result); - void OnAcceptGesture(FlutterMethodCall* call, FlutterResult result); - void OnRejectGesture(FlutterMethodCall* call, FlutterResult result); + void OnCreate(FlutterMethodCall* call, FlutterResult result) __attribute__((cf_audited_transfer)); + void OnDispose(FlutterMethodCall* call, FlutterResult result) + __attribute__((cf_audited_transfer)); + void OnAcceptGesture(FlutterMethodCall* call, FlutterResult result) + __attribute__((cf_audited_transfer)); + void OnRejectGesture(FlutterMethodCall* call, FlutterResult result) + __attribute__((cf_audited_transfer)); // Dispose the views in `views_to_dispose_`. void DisposeViews(); @@ -301,7 +308,7 @@ class FlutterPlatformViewsController { // Traverse the `mutators_stack` and return the number of clip operations. int CountClips(const MutatorsStack& mutators_stack); - void ClipViewSetMaskView(UIView* clipView); + void ClipViewSetMaskView(UIView* clipView) __attribute__((cf_audited_transfer)); // Applies the mutators in the mutators_stack to the UIView chain that was constructed by // `ReconstructClipViewsChain` @@ -314,7 +321,7 @@ class FlutterPlatformViewsController { // rect of the PlatformView, the clip mutator is not applied for performance optimization. void ApplyMutators(const MutatorsStack& mutators_stack, UIView* embedded_view, - const SkRect& bounding_rect); + const SkRect& bounding_rect) __attribute__((cf_audited_transfer)); void CompositeWithParams(int64_t view_id, const EmbeddedViewParams& params); diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.mm index ebdb0cc7a4979..859544e119a52 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.mm @@ -8,6 +8,8 @@ #include "flutter/fml/platform/darwin/cf_utils.h" #import "flutter/shell/platform/darwin/ios/ios_surface.h" +FLUTTER_ASSERT_ARC + static constexpr int kMaxPointsInVerb = 4; static constexpr NSUInteger kFlutterClippingMaskViewPoolCapacity = 5; @@ -75,7 +77,7 @@ @interface PlatformViewFilter () // `YES` if the backdropFilterView has been configured at least once. @property(nonatomic) BOOL backdropFilterViewConfigured; -@property(nonatomic, retain) UIVisualEffectView* backdropFilterView; +@property(nonatomic) UIVisualEffectView* backdropFilterView; // Updates the `visualEffectView` with the current filter parameters. // Also sets `self.backdropFilterView` to the updated visualEffectView. @@ -102,10 +104,9 @@ - (instancetype)initWithFrame:(CGRect)frame if (![PlatformViewFilter isUIVisualEffectViewImplementationValid]) { FML_DLOG(ERROR) << "Apple's API for UIVisualEffectView changed. Update the implementation to " "access the gaussianBlur CAFilter."; - [self release]; return nil; } - _backdropFilterView = [visualEffectView retain]; + _backdropFilterView = visualEffectView; _backdropFilterViewConfigured = NO; } return self; @@ -113,7 +114,6 @@ - (instancetype)initWithFrame:(CGRect)frame + (void)resetPreparation { _preparedOnce = NO; - [_gaussianBlurFilter release]; _gaussianBlurFilter = nil; _indexOfBackdropView = -1; _indexOfVisualEffectSubview = -1; @@ -130,7 +130,7 @@ + (void)prepareOnce:(UIVisualEffectView*)visualEffectView { for (NSObject* filter in view.layer.filters) { if ([[filter valueForKey:@"name"] isEqual:@"gaussianBlur"] && [[filter valueForKey:@"inputRadius"] isKindOfClass:[NSNumber class]]) { - _gaussianBlurFilter = [filter retain]; + _gaussianBlurFilter = filter; break; } } @@ -145,13 +145,6 @@ + (BOOL)isUIVisualEffectViewImplementationValid { return _indexOfBackdropView > -1 && _indexOfVisualEffectSubview > -1 && _gaussianBlurFilter; } -- (void)dealloc { - [_backdropFilterView release]; - _backdropFilterView = nil; - - [super dealloc]; -} - - (UIVisualEffectView*)backdropFilterView { FML_DCHECK(_backdropFilterView); if (!self.backdropFilterViewConfigured) { @@ -162,7 +155,7 @@ - (UIVisualEffectView*)backdropFilterView { } - (void)updateVisualEffectView:(UIVisualEffectView*)visualEffectView { - NSObject* gaussianBlurFilter = [[_gaussianBlurFilter copy] autorelease]; + NSObject* gaussianBlurFilter = [_gaussianBlurFilter copy]; FML_DCHECK(gaussianBlurFilter); UIView* backdropView = visualEffectView.subviews[_indexOfBackdropView]; [gaussianBlurFilter setValue:@(_blurRadius) forKey:@"inputRadius"]; @@ -179,8 +172,8 @@ - (void)updateVisualEffectView:(UIVisualEffectView*)visualEffectView { @interface ChildClippingView () -@property(retain, nonatomic) NSArray* filters; -@property(retain, nonatomic) NSMutableArray* backdropFilterSubviews; +@property(nonatomic, copy) NSArray* filters; +@property(nonatomic) NSMutableArray* backdropFilterSubviews; @end @@ -222,16 +215,6 @@ - (void)applyBlurBackdropFilters:(NSArray*)filters { } } -- (void)dealloc { - [_filters release]; - _filters = nil; - - [_backdropFilterSubviews release]; - _backdropFilterSubviews = nil; - - [super dealloc]; -} - - (NSMutableArray*)backdropFilterSubviews { if (!_backdropFilterSubviews) { _backdropFilterSubviews = [[NSMutableArray alloc] init]; @@ -459,11 +442,11 @@ @interface FlutterClippingMaskViewPool () // The maximum number of `FlutterClippingMaskView` the pool can contain. // This prevents the pool to grow infinately and limits the maximum memory a pool can use. -@property(assign, nonatomic) NSUInteger capacity; +@property(nonatomic) NSUInteger capacity; // The pool contains the views that are available to use. // The number of items in the pool must not excceds `capacity`. -@property(retain, nonatomic) NSMutableSet* pool; +@property(nonatomic) NSMutableSet* pool; @end @@ -483,11 +466,10 @@ - (FlutterClippingMaskView*)getMaskViewWithFrame:(CGRect)frame { FML_DCHECK(self.pool.count <= self.capacity); if (self.pool.count == 0) { // The pool is empty, alloc a new one. - return - [[[FlutterClippingMaskView alloc] initWithFrame:frame - screenScale:[UIScreen mainScreen].scale] autorelease]; + return [[FlutterClippingMaskView alloc] initWithFrame:frame + screenScale:UIScreen.mainScreen.scale]; } - FlutterClippingMaskView* maskView = [[[self.pool anyObject] retain] autorelease]; + FlutterClippingMaskView* maskView = [self.pool anyObject]; maskView.frame = frame; [maskView reset]; [self.pool removeObject:maskView]; @@ -503,10 +485,4 @@ - (void)insertViewToPoolIfNeeded:(FlutterClippingMaskView*)maskView { [self.pool addObject:maskView]; } -- (void)dealloc { - [_pool release]; - - [super dealloc]; -} - @end diff --git a/shell/platform/darwin/ios/framework/Source/FlutterView.mm b/shell/platform/darwin/ios/framework/Source/FlutterView.mm index 1576e0cb8e2a0..e50f903492f0a 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterView.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterView.mm @@ -6,8 +6,13 @@ #include "flutter/fml/platform/darwin/cf_utils.h" +FLUTTER_ASSERT_ARC + +@interface FlutterView () +@property(nonatomic, weak) id delegate; +@end + @implementation FlutterView { - id _delegate; BOOL _isWideGamutEnabled; } @@ -45,7 +50,7 @@ - (MTLPixelFormat)pixelFormat { return MTLPixelFormatBGRA8Unorm; } - (BOOL)isWideGamutSupported { - if (![_delegate isUsingImpeller]) { + if (!self.delegate.isUsingImpeller) { return NO; } @@ -62,7 +67,6 @@ - (instancetype)initWithDelegate:(id)delegate enableWideGamut:(BOOL)isWideGamutEnabled { if (delegate == nil) { NSLog(@"FlutterView delegate was nil."); - [self release]; return nil; } @@ -224,7 +228,7 @@ - (BOOL)isAccessibilityElement { // TODO(chunhtai): Remove this workaround once iOS provides an // API to query whether voice control is enabled. // https://github.com/flutter/flutter/issues/76808. - [_delegate flutterViewAccessibilityDidCall]; + [self.delegate flutterViewAccessibilityDidCall]; return NO; } diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewControllerTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewControllerTest.mm index 99bb2efc2a8d4..b87260c2907af 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewControllerTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewControllerTest.mm @@ -15,6 +15,7 @@ #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterEmbedderKeyResponder.h" #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterFakeKeyEvents.h" #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.h" +#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterView.h" #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h" #import "flutter/shell/platform/darwin/ios/framework/Source/UIViewController+FlutterScreenAndSceneIfLoaded.h" #import "flutter/shell/platform/darwin/ios/framework/Source/vsync_waiter_ios.h" @@ -1135,12 +1136,21 @@ - (void)testBinaryMessenger { - (void)testViewControllerIsReleased { __weak FlutterViewController* weakViewController; + __weak UIView* weakView; @autoreleasepool { - FlutterViewController* viewController = [[FlutterViewController alloc] init]; + FlutterEngine* engine = [[FlutterEngine alloc] init]; + + [engine runWithEntrypoint:nil]; + FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:engine + nibName:nil + bundle:nil]; weakViewController = viewController; [viewController viewDidLoad]; + weakView = viewController.view; + XCTAssertTrue([viewController.view isKindOfClass:[FlutterView class]]); } XCTAssertNil(weakViewController); + XCTAssertNil(weakView); } #pragma mark - Platform Brightness diff --git a/testing/ios/IosUnitTests/IosUnitTests.xcodeproj/project.pbxproj b/testing/ios/IosUnitTests/IosUnitTests.xcodeproj/project.pbxproj index 87f291b87dd38..66f378bcbb6b3 100644 --- a/testing/ios/IosUnitTests/IosUnitTests.xcodeproj/project.pbxproj +++ b/testing/ios/IosUnitTests/IosUnitTests.xcodeproj/project.pbxproj @@ -74,6 +74,7 @@ D2D361A52B234EAC0018964E /* FlutterMetalLayerTest.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = FlutterMetalLayerTest.mm; sourceTree = ""; }; F7521D7226BB671E005F15C5 /* libios_test_flutter.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libios_test_flutter.dylib; path = "../../../../out/$(FLUTTER_ENGINE)/libios_test_flutter.dylib"; sourceTree = ""; }; F7521D7526BB673E005F15C5 /* libocmock_shared.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libocmock_shared.dylib; path = "../../../../out/$(FLUTTER_ENGINE)/libocmock_shared.dylib"; sourceTree = ""; }; + F76A3A892BE48F2F00A654F1 /* FlutterPlatformViewsTest.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = FlutterPlatformViewsTest.mm; sourceTree = ""; }; F77E081726FA9CE6003E6E4C /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = "../../../../out/$(FLUTTER_ENGINE)/Flutter.framework"; sourceTree = ""; }; F7A3FDE026B9E0A300EADD61 /* FlutterAppDelegateTest.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = FlutterAppDelegateTest.mm; sourceTree = ""; }; /* End PBXFileReference section */ @@ -99,6 +100,7 @@ 0AC232E924BA71D300A85907 /* Source */ = { isa = PBXGroup; children = ( + F76A3A892BE48F2F00A654F1 /* FlutterPlatformViewsTest.mm */, 689EC1E2281B30D3008FEB58 /* FlutterSpellCheckPluginTest.mm */, 68B6091227F62F990036AC78 /* VsyncWaiterIosTest.mm */, 3DD7D38C27D2B81000DA365C /* FlutterUndoManagerPluginTest.mm */,