AVCaptureSession stopRunning方法创build可怕的挂起

使用来自iOS7教程第22章的Ray Wenderlich的QRCode阅读器 ,我成功阅读了当前应用程序的QRCodes。 我现在扩展它,成功读取一个QRCode,我想要存储的AVMetadataMachineReadableCodeObject读取AVMetadataMachineReadableCodeObjectAVMetadataMachineReadableCodeObject到一个新的视图,并在新的视图上使用该数据,或多或less大多数QRCode阅读器应用程序像RedLaser等)处理条形码和QRCodes。

但是,我调用[captureSession stopRunning] (这样它不会再读取QRCodes并触发更多的细节),并且有10多秒的挂起。 我试图通过这个SO问题来实现一个async调用,但无济于事。 我也看了这些 SO 问题 ,似乎不适合这个目的。

有没有人有一个想法如何消除这挂?

这里是代码:

 #import "BMQRCodeReaderViewController.h" #import "NSString+containsString.h" #import "BMManualExperimentDataEntryViewController.h" @import AVFoundation; @interface BMQRCodeReaderViewController () <AVCaptureMetadataOutputObjectsDelegate> @end @implementation BMQRCodeReaderViewController { AVCaptureSession *_captureSession; AVCaptureDevice *_videoDevice; AVCaptureDeviceInput *_videoInput; AVCaptureVideoPreviewLayer *_previewLayer; BOOL _running; AVCaptureMetadataOutput *_metadataOutput; } - (void)setupCaptureSession { // 1 if (_captureSession) return; // 2 _videoDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]; if (!_videoDevice) { NSLog(@"No video camera on this device!"); return; } // 3 _captureSession = [[AVCaptureSession alloc] init]; // 4 _videoInput = [[AVCaptureDeviceInput alloc] initWithDevice:_videoDevice error:nil]; // 5 if ([_captureSession canAddInput:_videoInput]) { [_captureSession addInput:_videoInput]; } // 6 _previewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:_captureSession]; _previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill; _metadataOutput = [[AVCaptureMetadataOutput alloc] init]; dispatch_queue_t metadataQueue = dispatch_queue_create("com.razeware.ColloQR.metadata", 0); [_metadataOutput setMetadataObjectsDelegate:self queue:metadataQueue]; if ([_captureSession canAddOutput:_metadataOutput]) { [_captureSession addOutput:_metadataOutput]; } } - (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection { // This fancy BOOL is just helping me fire the segue when the correct string is found __block NSNumber *didFind = [NSNumber numberWithBool:NO]; [metadataObjects enumerateObjectsUsingBlock:^(AVMetadataObject *obj, NSUInteger idx, BOOL *stop) { AVMetadataMachineReadableCodeObject *readableObject = (AVMetadataMachineReadableCodeObject *)obj; NSLog(@"Metadata: %@", readableObject); // [ containsString is a category I extended for NSString, just FYI if ([readableObject.stringValue containsString:@"CorrectString"]) { didFind = [NSNumber numberWithBool:YES]; NSLog(@"Found it"); _testName = @"NameOfTest"; *stop = YES; return; } }]; if ([didFind boolValue]) { NSLog(@"Confirming we found it"); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [self stopRunning]; }); _labelTestName.text = _testName; [self performSegueWithIdentifier:@"segueFromFoundQRCode" sender:self]; } else { NSLog(@"Did not find it"); } } - (void)startRunning { if (_running) return; [_captureSession startRunning]; _metadataOutput.metadataObjectTypes = _metadataOutput.availableMetadataObjectTypes; _running = YES; } - (void)stopRunning { if (!_running) return; [_captureSession stopRunning]; _running = NO; } - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. [self setupCaptureSession]; [self setupNavBar]; [self startRunning]; _previewLayer.frame = _previewView.bounds; [_previewView.layer addSublayer:_previewLayer]; } 

我已经先后解决了这个问题。 问题是委托方法调用

 - (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection 

正在后台运行。 这是用一个[NSThread isMainThread]调用来确定的,它失败了。

解决的办法是从QRCode中find正确的stringValue,在后台停止captureSession,然后在主线程中调用你的segue。 解决scheme看起来如此:

 - (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection { // This fancy BOOL is just helping me fire the segue when the correct string is found __block NSNumber *didFind = [NSNumber numberWithBool:NO]; [metadataObjects enumerateObjectsUsingBlock:^(AVMetadataObject *obj, NSUInteger idx, BOOL *stop) { AVMetadataMachineReadableCodeObject *readableObject = (AVMetadataMachineReadableCodeObject *)obj; NSLog(@"Metadata: %@", readableObject); if ([NSThread isMainThread]) { NSLog(@"Yes Main Thread"); } else { NSLog(@"Not main thread"); } // [ containsString is a category I extended for NSString, just FYI if ([readableObject.stringValue containsString:@"Biomeme"]) { //NSLog(@"this is a test: %@", getTestName); didFind = [NSNumber numberWithBool:YES]; NSLog(@"Found it"); _testName = readableObject.stringValue; *stop = YES; return; } }]; if ([didFind boolValue]) { NSLog(@"Confirming we found it"); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSDate *start = [NSDate date]; [self stopRunning]; NSLog(@"time took: %f", -[start timeIntervalSinceNow]); // *** Here is the key, make your segue on the main thread dispatch_async(dispatch_get_main_queue(), ^{ [self performSegueWithIdentifier:@"segueFromFoundQRCode" sender:self]; _labelTestName.text = _testName; }); }); } else { NSLog(@"Did not find it"); } }