static void * const MyAdjustingFocusObservationContext = (void*)&MyAdjustingFocusObservationContext; static void * const MyAdjustingExposureObservationContext = (void*)&MyAdjustingExposureObservationContext; -(void)focusAtPoint{ CGPoint point; if(fromRight) point.x = 450.0/480.0; else point.x = 30.0/480.0; point.y = 245.0/320.0; AVCaptureDevice *device =[AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]; if(device != nil) { NSError *error; if([device lockForConfiguration:&error]){ if([device isExposureModeSupported:AVCaptureFocusModeContinuousAutoFocus] && [device isFocusPointOfInterestSupported]) { [device setFocusPointOfInterest:point]; [device setFocusMode:AVCaptureFocusModeContinuousAutoFocus]; [device addObserver:self forKeyPath:@"adjustingFocus" options:NSKeyValueObservingOptionNew context:MyAdjustingFocusObservationContext]; NSLog(@"focus now"); } if([device isExposureModeSupported:AVCaptureExposureModeContinuousAutoExposure] && [device isExposurePointOfInterestSupported]) { [device setExposurePointOfInterest:point]; [device setExposureMode:AVCaptureExposureModeContinuousAutoExposure]; [device addObserver:self forKeyPath:@"adjustingExposure" options:NSKeyValueObservingOptionNew context:MyAdjustingExposureObservationContext]; NSLog(@"expose now"); } [device unlockForConfiguration]; }else{ NSLog(@"Error in Focus Mode"); } } } - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]; NSError *error; if([keyPath isEqualToString:@"adjustingFocus"]){ if(![object isAdjustingFocus]){ [device removeObserver:self forKeyPath:keyPath context:context]; if([device isFocusModeSupported:AVCaptureFocusModeLocked]) { [device lockForConfiguration:&error]; device.focusMode = AVCaptureFocusModeLocked; [device unlockForConfiguration]; NSLog(@" focus locked"); } } } if([keyPath isEqualToString:@"adjustingExposure"]){ if(![object isAdjustingExposure]){ [device removeObserver:self forKeyPath:keyPath context:context]; if([device isExposureModeSupported:AVCaptureExposureModeLocked]) { [device lockForConfiguration:&error]; device.exposureMode=AVCaptureExposureModeLocked; //causes the crash [device unlockForConfiguration]; NSLog(@" exposure locked"); } } }
如果我注释掉“device.exposureMode = AVCaptureExposureModeLocked”这一行,一切正常(除了焦点没有锁定).如果我将线条移动到焦点观察者,一切正常(除了曝光有时会在正确设置之前锁定).如果我以其他方式锁定曝光,例如通过计时器,它的工作原理.
崩溃日志对我没什么帮助(希望有人可以解释)
Exception Type: EXC_BAD_ACCESS (SIGSEGV) Exception Codes: KERN_INVALID_ADDRESS at 0x00000000 Crashed Thread: 0 Thread 0 name: Dispatch queue: com.apple.main-thread Thread 0 Crashed: 0 Foundation 0x3209d5e2 NSKVOPendingNotificationRelease + 6 1 CoreFoundation 0x317b21c8 __CFArrayReleaseValues + 352 2 CoreFoundation 0x317419f8 _CFArrayReplaceValues + 308 3 CoreFoundation 0x3174391c CFArrayRemoveValueAtIndex + 80 4 Foundation 0x3209d6b6 NSKeyValuePopPendingNotificationPerThread + 38 5 Foundation 0x32090328 NSKeyValueDidChange + 356 6 Foundation 0x3206a6ce -[NSObject(NSKeyValueObserverNotification) didChangeValueForKey:] + 90 7 AVFoundation 0x30989fd0 -[AVCaptureFigVideoDevice handleNotification:payload:] + 1668 8 AVFoundation 0x30983f60 -[AVCaptureDeviceInput handleNotification:payload:] + 84 9 AVFoundation 0x3098fc64 avcaptureSessionFigRecorderNotification + 924 10 AVFoundation 0x309b1c64 AVCMNotificationDispatcherCallback + 188 11 CoreFoundation 0x317cee22 __CFNotificationCenterAddObserver_block_invoke_0 + 122 12 CoreFoundation 0x31753034 _CFXNotificationPost + 1424 13 CoreFoundation 0x3175460c CFNotificationCenterPostNotification + 100 14 CoreMedia 0x31d3db8e CMNotificationCenterPostNotification + 114 15 Celestial 0x34465aa4 FigRecorderRemoteCallbacksServer_NotificationIsPending + 628 16 Celestial 0x34465826 _XNotificationIsPending + 66 17 Celestial 0x344657dc figrecordercallbacks_server + 96 18 Celestial 0x34465028 remrec_ClientPortCallBack + 172 19 CoreFoundation 0x317cc5d8 __CFMachPortPerform + 116 20 CoreFoundation 0x317d7170 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ + 32 21 CoreFoundation 0x317d7112 __CFRunLoopDoSource1 + 134 22 CoreFoundation 0x317d5f94 __CFRunLoopRun + 1380 23 CoreFoundation 0x31748eb8 CFRunLoopRunSpecific + 352 24 CoreFoundation 0x31748d44 CFRunLoopRunInMode + 100 25 GraphicsServices 0x3530c2e6 GSEventRunModal + 70 26 UIKit 0x3365e2fc UIApplicationMain + 1116 27 ShootKing 0x000ed304 main (main.m:16) 28 ShootKing 0x000ed28c start + 36
解决方法
另外,请注意,在您发布的代码中,您没有使用上下文来识别您的观察结果,并且您没有在observeValueForKeyPath:…实现中调用super.这两件事都可能导致细微的,难以诊断的错误. KVO的防弹模式看起来像这样:
static void * const MyAdjustingFocusObservationContext = (void*)&MyAdjustingFocusObservationContext; static void * const MyAdjustingExposureObservationContext = (void*)&MyAdjustingExposureObservationContext; - (void)focusAtPoint { // ... other stuff ... [device addObserver:self forKeyPath:@"adjustingFocus" options:NSKeyValueObservingOptionNew context:MyAdjustingFocusObservationContext]; [device addObserver:self forKeyPath:@"adjustingExposure" options:NSKeyValueObservingOptionNew context:MyAdjustingExposureObservationContext]; // ... other stuff ... } - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { if (context == MyAdjustingFocusObservationContext) { // Do stuff } else if (context == MyAdjustingExposureObservationContext) { // Do other stuff } else { [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; } }
编辑:我想跟进,看看我是否可以在这种特定情况下提供更多帮助.我从代码和你的评论中收集到你正在寻找这些观察结果,实际上是一次性的.我看到两种方法:
对于这个对象来说,总是观察捕获设备(即addObserver:…当你初始化时,removeObserver:…当你dealloc时),然后使用一些名为“门”的行为“ waitingForFocus和waitingForExposure.在-focusAtPoint当前addObserver:…而是将ivars设置为YES.然后在observeValueForKeyPath:…只有当这些ivars为YES然后而不是removeObserver时才采取行动:…只需将ivars设置为NO.这应该具有所需的效果,而不需要每次添加和删除观察.
我想到的另一种方法是使用GCD调用removeObserver:…“以后”.所以你会改变removeObserver:…像这样:
dispatch_async(dispatch_get_main_queue(),^{ [device removeObserver:self forKeyPath:keyPath context:context]; });
这将导致在通知过程完成后在运行循环中的其他位置进行调用.这稍微不那么防弹,因为没有什么可以保证在发生延迟删除调用之前第二次不会触发通知.在这方面,第一种方法在实现期望的一次性行为方面更严格地“正确”.
编辑2:我不能放手. :)我弄清楚你为什么会崩溃.我观察到,在用于adjustExposure的KVO处理程序中设置exposureMode最终会导致另一个adjustExposure通知,因此堆栈会一直爆炸,直到您的进程被终止.我能够通过包装observeValueForKeyPath的部分来实现它的工作:…处理dispatch_async(dispatch_get_main_queue(),^ {…})中adjustExposure的更改; (包括最终的removeObserver:…调用).在此之后它对我有用,并且肯定锁定曝光和焦点.也就是说,就像我上面提到的那样,这可以用ivars更好地处理以防止递归而不是任意延迟的dispatch_async().
希望有所帮助.