Ios AVCaptureSession stopRunning方法创建了严重的挂起

Ios AVCaptureSession stopRunning方法创建了严重的挂起,ios,objective-c,avfoundation,avcapturesession,Ios,Objective C,Avfoundation,Avcapturesession,使用,我正在成功读取当前应用程序的QRCodes。我现在扩展它,在成功读取QRCode后,我想存储已读取的AVMetadataMachineReadableCodeObject的stringValue,切换到新视图,并在新视图中使用该数据,或多或少地了解大多数QRCode阅读器应用程序(如……等)处理条形码和QRCode的方式 但是,我调用[captureSession stopRunning](这样它就不会读取任何QRCodes并触发额外的序列),并且会有10秒以上的挂起。我曾尝试实现一个as

使用,我正在成功读取当前应用程序的QRCodes。我现在扩展它,在成功读取QRCode后,我想存储已读取的
AVMetadataMachineReadableCodeObject
stringValue
,切换到新视图,并在新视图中使用该数据,或多或少地了解大多数QRCode阅读器应用程序(如……等)处理条形码和QRCode的方式

但是,我调用
[captureSession stopRunning]
(这样它就不会读取任何QRCodes并触发额外的序列),并且会有10秒以上的挂起。我曾尝试实现一个
async
调用per,但是没有效果。我也研究过,它们似乎不适合于这一目的

有人知道如何移除这个悬挂物吗

代码如下:

#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];
}
#导入“BMQRCodeReaderViewController.h”
#导入“NSString+containsString.h”
#导入“BMManualExperimentDataEntryViewController.h”
@进口基金会;
@接口BMQRCodeReaderViewController()
@结束
@BMQRCodeReaderViewController的实现{
AVCaptureSession*_captureSession;
AVCaptureDevice*\u视频设备;
AVCaptureDeviceInput*\u视频输入;
AVCaptureVideoPreviewLayer*\u previewLayer;
跑步;
AVCaptureMataOutput*_metadataOutput;
}
-(无效)setupCaptureSession{//1
如果(_captureSession)返回;
// 2
_videoDevice=[AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
如果(!\u视频设备){
NSLog(@“此设备上没有摄像头!”);返回;
}
// 3
_captureSession=[[AVCaptureSession alloc]init];
// 4
_videoInput=[[AvCaptureDeviceInputAlloc]initWithDevice:\u videoDevice错误:nil];
// 5
如果([[U captureSession canAddInput:[U videoInput]){[U captureSession addInput:[U videoInput];
}
// 6
_previewLayer=[[AVCaptureVideoPreviewLayer alloc]initWithSession:_captureSession];
_previewLayer.videoGravity=AVLayerVideoGravityResizeAspectFill;
_metadataOutput=[[AvCaptureMataOutput alloc]init];
dispatch_queue_t metadataQueue=dispatch_queue_create(“com.razeware.ColloQR.metadata”,0);
[_MetadataOutputSetMetadataObjectsDelegate:自队列:metadataQueue];
如果([\u captureSession CanadOutput:\u metadataOutput]){[\u captureSession addOutput:\u metadataOutput];
}
}
-(void)captureOutput:(AVCaptureOutput*)captureOutput didOutputMetadataObjects:(NSArray*)metadataObjects
fromConnection:(AVCaptureConnection*)连接{
//当找到正确的字符串时,这个花哨的BOOL正帮我启动segue
__块NSNumber*didFind=[NSNumber numberWithBool:NO];
[metadataObjects enumerateObjectsUsingBlock:^(AVMetadataObject*obj,NSUInteger idx,BOOL*stop){
AVMetadataMachineReadableCodeObject*readableObject=(AVMetadataMachineReadableCodeObject*)对象;
NSLog(@“元数据:%@”,readableObject);
//[containsString是我为NSString扩展的一个类别,仅供参考
if([readableObject.stringValue包含字符串:@“CorrectString”]){
didFind=[NSNumber numberWithBool:是];
NSLog(@“找到了”);
_testName=@“测试名称”;
*停止=是;
返回;
}
}];
if([didFind布尔值]){
NSLog(“确认我们找到了”);
调度异步(调度获取全局队列(调度队列优先级默认为0)^{
[自动停止运行];
});
_labelTestName.text=\u testName;
[self-PerformsgueWithIdentifier:@“segueFromFoundQRCode”发送方:self];
}
否则{
NSLog(@“未找到它”);
}
}
-(空)开始耳轴{
如果(_正在运行)
返回;
[_CaptureSessionStartrunning];
_metadataOutput.metadataObjectTypes=\u metadataOutput.availableMetadataObjectTypes;
_运行=是;
}
-(无效)停止运行{
如果(!\u运行)返回;
[\u captureSession停止运行];
_运行=否;
}
-(无效)viewDidLoad
{
[超级视图下载];
//加载视图后执行任何其他设置。
[自我设置捕获会话];
[自设置导航栏];
[自起动耳轴];
_previewLayer.frame=\u previewView.bounds;
[[u previewView.layer addSublayer:[u previewLayer];
}

我已经成功地解决了这个问题。问题是委托方法调用

- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects
       fromConnection:(AVCaptureConnection *)connection
正在后台运行。这是通过调用
[NSThread isMainThread]
确定的,调用失败

解决方案是从QRCode中找到正确的stringValue,在后台停止captureSession,然后在主线程上调用segue。解决方案如下所示:

- (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");
    }
}

这是一个在StackOverflow上没有太多涉及的主题,看起来,这很有帮助,谢谢。有一件事,我现在收到消息终止应用程序崩溃,原因是未捕获的异常“NSInternalInconsistencyException”,原因是:“仅在主线程上运行!”你认为是我的prepareForsegue造成的吗?嗨@Alioo,很抱歉由于时间太晚,我已经有一段时间没有重温这个代码了(我们已经有一段时间没有做QR了),但是我遇到了同样的问题--可能需要重新考虑在后台运行此方法,或者只是等待启动segue。通过一些修补,我认为我们可以在较少的问题下执行此操作。噢!@Max我实际上解决了这个问题,我需要尽快重新检查代码,所以当我记得我所做的事情时,我会很高兴我会把它贴在这里,花了很多时间才搞定!非常感谢你,这太棒了,也是我在网上找到的唯一答案。