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.
diff --git a/shell/platform/darwin/ios/BUILD.gn b/shell/platform/darwin/ios/BUILD.gn
index 2ddfd93..e6b6c9f 100644
--- a/shell/platform/darwin/ios/BUILD.gn
+++ b/shell/platform/darwin/ios/BUILD.gn
@@ -73,6 +73,11 @@
     "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 @@
     "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 @@
     "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 dc29cd8..2e98396 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 41295f2..46aafd8 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 @@
       overlay_view.reset([[FlutterOverlayView alloc] init]);
       overlay_view_wrapper.reset([[FlutterOverlayView alloc] init]);
 
-      auto ca_layer = fml::scoped_nsobject<CALayer>{[[overlay_view.get() layer] retain]};
+      auto ca_layer = fml::scoped_nsobject<CALayer>{[overlay_view.get() layer]};
       std::unique_ptr<IOSSurface> ios_surface = IOSSurface::Create(ios_context, ca_layer);
       std::unique_ptr<Surface> surface = ios_surface->CreateGPUSurface();
 
@@ -105,7 +107,7 @@
       overlay_view_wrapper.reset([[FlutterOverlayView alloc] initWithContentsScale:screenScale
                                                                        pixelFormat:pixel_format]);
 
-      auto ca_layer = fml::scoped_nsobject<CALayer>{[[overlay_view.get() layer] retain]};
+      auto ca_layer = fml::scoped_nsobject<CALayer>{[overlay_view.get() layer]};
       std::unique_ptr<IOSSurface> ios_surface = IOSSurface::Create(ios_context, ca_layer);
       std::unique_ptr<Surface> surface = ios_surface->CreateGPUSurface(gr_context);
 
@@ -159,12 +161,12 @@
 }
 
 void FlutterPlatformViewsController::SetFlutterView(UIView* flutter_view) {
-  flutter_view_.reset([flutter_view retain]);
+  flutter_view_.reset(flutter_view);
 }
 
 void FlutterPlatformViewsController::SetFlutterViewController(
     UIViewController<FlutterViewResponder>* flutter_view_controller) {
-  flutter_view_controller_.reset([flutter_view_controller retain]);
+  flutter_view_controller_.reset(flutter_view_controller);
 }
 
 UIViewController<FlutterViewResponder>* FlutterPlatformViewsController::getFlutterViewController() {
@@ -231,21 +233,19 @@
   // 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<NSObject<FlutterPlatformView>>([embedded_view retain]);
+  views_[viewId] = fml::scoped_nsobject<NSObject<FlutterPlatformView>>(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<FlutterTouchInterceptingView>([touch_interceptor retain]);
+      fml::scoped_nsobject<FlutterTouchInterceptingView>(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<UIView>([clipping_view retain]);
+  root_views_[viewId] = fml::scoped_nsobject<UIView>(clipping_view);
 
   result(nil);
 }
@@ -307,8 +307,7 @@
     FlutterPlatformViewGestureRecognizersBlockingPolicy gestureRecognizerBlockingPolicy) {
   std::string idString([factoryId UTF8String]);
   FML_CHECK(factories_.count(idString) == 0);
-  factories_[idString] =
-      fml::scoped_nsobject<NSObject<FlutterPlatformViewFactory>>([factory retain]);
+  factories_[idString] = fml::scoped_nsobject<NSObject<FlutterPlatformViewFactory>>(factory);
   gesture_recognizers_blocking_policies_[idString] = gestureRecognizerBlockingPolicy;
 }
 
@@ -417,7 +416,7 @@
 
 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 @@
   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 @@
         // 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 @@
 
 // Indicates that if the `DelayingGestureRecognizer`'s state should be set to
 // `UIGestureRecognizerStateEnded` during next `touchesEnded` call.
-@property(nonatomic) bool shouldEndInNextTouchesEnded;
+@property(nonatomic) BOOL shouldEndInNextTouchesEnded;
 
 // Indicates that the `DelayingGestureRecognizer`'s `touchesEnded` has been invoked without
 // setting the state to `UIGestureRecognizerStateEnded`.
-@property(nonatomic) bool touchedEndedWithoutBlocking;
+@property(nonatomic) BOOL touchedEndedWithoutBlocking;
+
+@property(nonatomic, readonly) UIGestureRecognizer* forwardingRecognizer;
 
 - (instancetype)initWithTarget:(id)target
                         action:(SEL)action
@@ -977,11 +977,13 @@
            (fml::WeakPtr<flutter::FlutterPlatformViewsController>)platformViewsController;
 @end
 
-@implementation FlutterTouchInterceptingView {
-  fml::scoped_nsobject<DelayingGestureRecognizer> _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<flutter::FlutterPlatformViewsController>)platformViewsController
@@ -996,47 +998,42 @@
 
     [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 @@
 }
 
 - (id)accessibilityContainer {
-  return _flutterAccessibilityContainer;
-}
-
-- (void)dealloc {
-  [_flutterAccessibilityContainer release];
-  [super dealloc];
+  return self.flutterAccessibilityContainer;
 }
 
 @end
 
-@implementation DelayingGestureRecognizer {
-  fml::scoped_nsobject<UIGestureRecognizer> _forwardingRecognizer;
-}
+@implementation DelayingGestureRecognizer
 
 - (instancetype)initWithTarget:(id)target
                         action:(SEL)action
@@ -1082,9 +1072,9 @@
     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 @@
     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 @@
     // 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 c2357c3..6373e1f 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 @@
 
   fml::WeakPtr<flutter::FlutterPlatformViewsController> GetWeakPtr();
 
-  void SetFlutterView(UIView* flutter_view);
+  void SetFlutterView(UIView* flutter_view) __attribute__((cf_audited_transfer));
 
-  void SetFlutterViewController(UIViewController<FlutterViewResponder>* flutter_view_controller);
+  void SetFlutterViewController(UIViewController<FlutterViewResponder>* flutter_view_controller)
+      __attribute__((cf_audited_transfer));
 
-  UIViewController<FlutterViewResponder>* getFlutterViewController();
+  UIViewController<FlutterViewResponder>* getFlutterViewController()
+      __attribute__((cf_audited_transfer));
 
   void RegisterViewFactory(
       NSObject<FlutterPlatformViewFactory>* 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 @@
                    const std::shared_ptr<IOSContext>& ios_context,
                    std::unique_ptr<SurfaceFrame> 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 @@
 
   using LayersMap = std::map<int64_t, std::vector<std::shared_ptr<FlutterPlatformViewLayer>>>;
 
-  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 @@
   // 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 @@
   // 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 ebdb0cc..859544e 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 @@
 
 // `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 @@
     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 @@
 
 + (void)resetPreparation {
   _preparedOnce = NO;
-  [_gaussianBlurFilter release];
   _gaussianBlurFilter = nil;
   _indexOfBackdropView = -1;
   _indexOfVisualEffectSubview = -1;
@@ -130,7 +130,7 @@
       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 @@
   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 @@
 }
 
 - (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 @@
 
 @interface ChildClippingView ()
 
-@property(retain, nonatomic) NSArray<PlatformViewFilter*>* filters;
-@property(retain, nonatomic) NSMutableArray<UIVisualEffectView*>* backdropFilterSubviews;
+@property(nonatomic, copy) NSArray<PlatformViewFilter*>* filters;
+@property(nonatomic) NSMutableArray<UIVisualEffectView*>* backdropFilterSubviews;
 
 @end
 
@@ -222,16 +215,6 @@
   }
 }
 
-- (void)dealloc {
-  [_filters release];
-  _filters = nil;
-
-  [_backdropFilterSubviews release];
-  _backdropFilterSubviews = nil;
-
-  [super dealloc];
-}
-
 - (NSMutableArray*)backdropFilterSubviews {
   if (!_backdropFilterSubviews) {
     _backdropFilterSubviews = [[NSMutableArray alloc] init];
@@ -459,11 +442,11 @@
 
 // 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<FlutterClippingMaskView*>* pool;
+@property(nonatomic) NSMutableSet<FlutterClippingMaskView*>* pool;
 
 @end
 
@@ -483,11 +466,10 @@
   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 @@
   [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 1576e0c..e50f903 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<FlutterViewEngineDelegate> delegate;
+@end
+
 @implementation FlutterView {
-  id<FlutterViewEngineDelegate> _delegate;
   BOOL _isWideGamutEnabled;
 }
 
@@ -45,7 +50,7 @@
   return MTLPixelFormatBGRA8Unorm;
 }
 - (BOOL)isWideGamutSupported {
-  if (![_delegate isUsingImpeller]) {
+  if (!self.delegate.isUsingImpeller) {
     return NO;
   }
 
@@ -62,7 +67,6 @@
                  enableWideGamut:(BOOL)isWideGamutEnabled {
   if (delegate == nil) {
     NSLog(@"FlutterView delegate was nil.");
-    [self release];
     return nil;
   }
 
@@ -224,7 +228,7 @@
   // 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 99bb2ef..b87260c 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)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 87f291b..66f378b 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 = "<group>"; };
 		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 = "<group>"; };
 		F7521D7526BB673E005F15C5 /* libocmock_shared.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libocmock_shared.dylib; path = "../../../../out/$(FLUTTER_ENGINE)/libocmock_shared.dylib"; sourceTree = "<group>"; };
+		F76A3A892BE48F2F00A654F1 /* FlutterPlatformViewsTest.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = FlutterPlatformViewsTest.mm; sourceTree = "<group>"; };
 		F77E081726FA9CE6003E6E4C /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = "../../../../out/$(FLUTTER_ENGINE)/Flutter.framework"; sourceTree = "<group>"; };
 		F7A3FDE026B9E0A300EADD61 /* FlutterAppDelegateTest.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = FlutterAppDelegateTest.mm; sourceTree = "<group>"; };
 /* End PBXFileReference section */
@@ -99,6 +100,7 @@
 		0AC232E924BA71D300A85907 /* Source */ = {
 			isa = PBXGroup;
 			children = (
+				F76A3A892BE48F2F00A654F1 /* FlutterPlatformViewsTest.mm */,
 				689EC1E2281B30D3008FEB58 /* FlutterSpellCheckPluginTest.mm */,
 				68B6091227F62F990036AC78 /* VsyncWaiterIosTest.mm */,
 				3DD7D38C27D2B81000DA365C /* FlutterUndoManagerPluginTest.mm */,