-
Notifications
You must be signed in to change notification settings - Fork 9.7k
[camera]use weak self to fix a crash due to orientation change #6277
Changes from all commits
c3d142d
d2df7d3
4fdb87d
9f87dba
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -19,7 +19,6 @@ | |
| @interface CameraPlugin () | ||
| @property(readonly, nonatomic) FLTThreadSafeTextureRegistry *registry; | ||
| @property(readonly, nonatomic) NSObject<FlutterBinaryMessenger> *messenger; | ||
| @property(readonly, nonatomic) FLTThreadSafeMethodChannel *deviceEventMethodChannel; | ||
| @end | ||
|
|
||
| @implementation CameraPlugin | ||
|
|
@@ -56,6 +55,10 @@ - (void)initDeviceEventMethodChannel { | |
| [[FLTThreadSafeMethodChannel alloc] initWithMethodChannel:methodChannel]; | ||
| } | ||
|
|
||
| - (void)detachFromEngineForRegistrar:(NSObject<FlutterPluginRegistrar> *)registrar { | ||
| [UIDevice.currentDevice endGeneratingDeviceOrientationNotifications]; | ||
| } | ||
|
|
||
| - (void)startOrientationListener { | ||
| [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications]; | ||
| [[NSNotificationCenter defaultCenter] addObserver:self | ||
|
|
@@ -73,11 +76,12 @@ - (void)orientationChanged:(NSNotification *)note { | |
| return; | ||
| } | ||
|
|
||
| __weak typeof(self) weakSelf = self; | ||
| dispatch_async(self.captureSessionQueue, ^{ | ||
| // `FLTCam::setDeviceOrientation` must be called on capture session queue. | ||
| [self.camera setDeviceOrientation:orientation]; | ||
| [weakSelf.camera setDeviceOrientation:orientation]; | ||
| // `CameraPlugin::sendDeviceOrientation` can be called on any queue. | ||
| [self sendDeviceOrientation:orientation]; | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This line is the root cause, but i also went through all other usage of "strong self"s in case of similar crashes in the future More detailed discussion can be found here where I explained why orientation change is identified as the root cause. |
||
| [weakSelf sendDeviceOrientation:orientation]; | ||
| }); | ||
| } | ||
|
|
||
|
|
@@ -89,11 +93,11 @@ - (void)sendDeviceOrientation:(UIDeviceOrientation)orientation { | |
|
|
||
| - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result { | ||
| // Invoke the plugin on another dispatch queue to avoid blocking the UI. | ||
| dispatch_async(_captureSessionQueue, ^{ | ||
| __weak typeof(self) weakSelf = self; | ||
| dispatch_async(self.captureSessionQueue, ^{ | ||
| FLTThreadSafeFlutterResult *threadSafeResult = | ||
| [[FLTThreadSafeFlutterResult alloc] initWithResult:result]; | ||
|
|
||
| [self handleMethodCallAsync:call result:threadSafeResult]; | ||
| [weakSelf handleMethodCallAsync:call result:threadSafeResult]; | ||
| }); | ||
| } | ||
|
|
||
|
|
@@ -261,7 +265,11 @@ - (void)handleMethodCallAsync:(FlutterMethodCall *)call | |
| - (void)handleCreateMethodCall:(FlutterMethodCall *)call | ||
| result:(FLTThreadSafeFlutterResult *)result { | ||
| // Create FLTCam only if granted camera access (and audio access if audio is enabled) | ||
| __weak typeof(self) weakSelf = self; | ||
| FLTRequestCameraPermissionWithCompletionHandler(^(FlutterError *error) { | ||
| typeof(self) strongSelf = weakSelf; | ||
| if (!strongSelf) return; | ||
|
|
||
| if (error) { | ||
| [result sendFlutterError:error]; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If self is |
||
| } else { | ||
|
|
@@ -272,22 +280,29 @@ - (void)handleCreateMethodCall:(FlutterMethodCall *)call | |
| if (audioEnabled) { | ||
| // Setup audio capture session only if granted audio access. | ||
| FLTRequestAudioPermissionWithCompletionHandler(^(FlutterError *error) { | ||
| // cannot use the outter `strongSelf` | ||
| typeof(self) strongSelf = weakSelf; | ||
| if (!strongSelf) return; | ||
| if (error) { | ||
| [result sendFlutterError:error]; | ||
| } else { | ||
| [self createCameraOnSessionQueueWithCreateMethodCall:call result:result]; | ||
| [strongSelf createCameraOnSessionQueueWithCreateMethodCall:call result:result]; | ||
| } | ||
| }); | ||
| } else { | ||
| [self createCameraOnSessionQueueWithCreateMethodCall:call result:result]; | ||
| [strongSelf createCameraOnSessionQueueWithCreateMethodCall:call result:result]; | ||
| } | ||
| } | ||
| }); | ||
| } | ||
|
|
||
| - (void)createCameraOnSessionQueueWithCreateMethodCall:(FlutterMethodCall *)createMethodCall | ||
| result:(FLTThreadSafeFlutterResult *)result { | ||
| __weak typeof(self) weakSelf = self; | ||
| dispatch_async(self.captureSessionQueue, ^{ | ||
| typeof(self) strongSelf = weakSelf; | ||
| if (!strongSelf) return; | ||
|
|
||
| NSString *cameraName = createMethodCall.arguments[@"cameraName"]; | ||
| NSString *resolutionPreset = createMethodCall.arguments[@"resolutionPreset"]; | ||
| NSNumber *enableAudio = createMethodCall.arguments[@"enableAudio"]; | ||
|
|
@@ -296,22 +311,22 @@ - (void)createCameraOnSessionQueueWithCreateMethodCall:(FlutterMethodCall *)crea | |
| resolutionPreset:resolutionPreset | ||
| enableAudio:[enableAudio boolValue] | ||
| orientation:[[UIDevice currentDevice] orientation] | ||
| captureSessionQueue:self.captureSessionQueue | ||
| captureSessionQueue:strongSelf.captureSessionQueue | ||
| error:&error]; | ||
|
|
||
| if (error) { | ||
| [result sendError:error]; | ||
| } else { | ||
| if (self.camera) { | ||
| [self.camera close]; | ||
| if (strongSelf.camera) { | ||
| [strongSelf.camera close]; | ||
| } | ||
| self.camera = cam; | ||
| [self.registry registerTexture:cam | ||
| completion:^(int64_t textureId) { | ||
| [result sendSuccessWithData:@{ | ||
| @"cameraId" : @(textureId), | ||
| }]; | ||
| }]; | ||
| strongSelf.camera = cam; | ||
| [strongSelf.registry registerTexture:cam | ||
| completion:^(int64_t textureId) { | ||
| [result sendSuccessWithData:@{ | ||
| @"cameraId" : @(textureId), | ||
| }]; | ||
| }]; | ||
| } | ||
| }); | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -21,8 +21,11 @@ - (instancetype)initWithEventChannel:(FlutterEventChannel *)channel { | |
|
|
||
| - (void)setStreamHandler:(NSObject<FlutterStreamHandler> *)handler | ||
| completion:(void (^)(void))completion { | ||
| __weak typeof(self) weakSelf = self; | ||
| FLTEnsureToRunOnMainQueue(^{ | ||
| [self.channel setStreamHandler:handler]; | ||
| typeof(self) strongSelf = weakSelf; | ||
| if (!strongSelf) return; | ||
| [strongSelf.channel setStreamHandler:handler]; | ||
| completion(); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should the completion be run if this is dead?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Me being lazy. Should've used Looking at the current usage, running completion should be fine, but I think it's better not to run it. |
||
| }); | ||
| } | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did not clear out
inProgressSavePhotoDelegatessince the delegate does not hold strong reference to its owner anymore (deleted the outdated comment here)