Hello,
I'm trying to address the following crash from an old Objective-C code.
Crashed: com.apple.main-thread
0 libobjc.A.dylib 0x88e8 object_isClass + 16
1 Foundation 0x1d748 KVO_IS_RETAINING_ALL_OBSERVERS_OF_THIS_OBJECT_IF_IT_CRASHES_AN_OBSERVER_WAS_OVERRELEASED_OR_SMASHED + 48
2 Foundation 0x2be10 -[NSObject(NSKeyValueObservingPrivate) _changeValueForKeys:count:maybeOldValuesDict:maybeNewValuesDict:usingBlock:] + 284
3 Foundation 0x21c5c -[NSObject(NSKeyValueObservingPrivate) _changeValueForKey:key:key:usingBlock:] + 72
4 Foundation 0x2daf8 _NSSetBoolValueAndNotify + 316
5 SDKCore 0x47854 __45-[CameraSession processPhoto:orientation:]_block_invoke_3 + 239 (CameraSession.m:239)
6 libdispatch.dylib 0x2914 _dispatch_call_block_and_release + 32
7 libdispatch.dylib 0x4660 _dispatch_client_callout + 20
8 libdispatch.dylib 0x12b60 _dispatch_main_queue_callback_4CF + 944
9 CoreFoundation 0x51cd4 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 16
10 CoreFoundation 0xbeac __CFRunLoopRun + 2540
11 CoreFoundation 0x1f3b8 CFRunLoopRunSpecific + 600
12 GraphicsServices 0x138c GSEventRunModal + 164
13 UIKitCore 0x5196a8 -[UIApplication _run] + 1100
14 UIKitCore 0x2987f4 UIApplicationMain + 2092
15 MyApp 0x79bc main + 11 (main.swift:11)
16 ??? 0x102161a24 (Missing)
The crash is due to setting takingPhoto
to NO
in - (void)processPhoto:(AVCapturePhoto *)photo orientation:(UIDeviceOrientation)orientation
.
From what I understand, the crash would be caused by a released observer receiving the notification, but I don't see how it's possible given the following code (this is an excerpt with relevant parts).
@implementation CameraSession
@synthesize takingPhoto = _takingPhoto;
- (void)dealloc {
[self _cleanupObservers];
}
- (instancetype)init {
self = [super init];
if (self) {
[self _setupObservers];
}
return self;
}
- (void)setTakingPhoto:(BOOL)takingPhoto {
if (!takingPhoto) {
[self.triggerDecider reset];
}
_takingPhoto = takingPhoto;
}
- (BOOL)isTakingPhoto {
return _takingPhoto;
}
- (void)takePhoto {
if (self.isTakingPhoto) {
return;
}
self.takingPhoto = YES;
// …
[self.capturePhotoOutput capturePhotoWithSettings:[self buildCapturePhotoSettings] delegate:self];
}
- (void)processPhoto:(AVCapturePhoto *)photo orientation:(UIDeviceOrientation)orientation {
dispatch_async(self.captureSessionQueue, ^{
[self.delegate cameraSessionDidSnapPhoto:self];
[self.cameraCaptureHandler processPhoto:photo orientation:orientation onSuccess:^(Scan *scan) {
dispatch_async(dispatch_get_main_queue(), ^{
self.takingPhoto = NO;
[self.delegate cameraSession:self didGenerateScan:scan];
});
} failure:^(NSError *error) {
self.takingPhoto = NO;
[self.delegate cameraSession:self didFailToSnapPhotoWithError:error];
}];
});
}
#pragma mark - KVO
- (void)_setupObservers {
[self addObserver:self forKeyPath:@"takingPhoto" options:NSKeyValueObservingOptionInitial|NSKeyValueObservingOptionNew context:CameraSessionKVOContext];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(captureSessionRuntimeError:) name:AVCaptureSessionRuntimeErrorNotification object:self.captureSession];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(captureSessionDidStartRunning:) name:AVCaptureSessionDidStartRunningNotification object:self.captureSession];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(captureSessionDidStopRunning:) name:AVCaptureSessionDidStopRunningNotification object:self.captureSession];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(captureSessionWasInterrupted:) name:AVCaptureSessionWasInterruptedNotification object:self.captureSession];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(captureSessionInterruptionEnded:) name:AVCaptureSessionInterruptionEndedNotification object:self.captureSession];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidEnterBackground) name:UIApplicationDidEnterBackgroundNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationWillEnterForeground) name:UIApplicationWillEnterForegroundNotification object:nil];
}
- (void)_cleanupObservers {
[self removeObserver:self forKeyPath:@"takingPhoto" context:CameraSessionKVOContext];
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
if (context == CameraSessionKVOContext) {
if (object == self && [keyPath isEqualToString:@"takingPhoto"]) {
// While taking the photo, stop processing video frames
// ...
}
} else {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
@end
Am I missing something here?
Thanks! Bruno