Crash "KVO_IS_RETAINING_ALL_OBSERVERS_OF_THIS_OBJECT_IF_IT_CRASHES_AN_OBSERVER_WAS_OVERRELEASED_OR_SMASHED"

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

Interesting that no one from Apple has jumped in to say something here.... I am facing a similar crash and the stack trace is not very descriptive of what the issue could be....

Crash "KVO_IS_RETAINING_ALL_OBSERVERS_OF_THIS_OBJECT_IF_IT_CRASHES_AN_OBSERVER_WAS_OVERRELEASED_OR_SMASHED"
 
 
Q