我在q1上的dispatch_async调用主队列上的UI对象,它在q1上添加回调,崩溃

时间:2013-01-29 16:51:36

标签: objective-c multithreading objective-c-blocks grand-central-dispatch

听起来令人困惑,但看起来像这样

AVPlayer *capturedPlayer = _player;
dispatch_async(_subtitlesQueue, ^{
    // Parse the requested subtitle track and create a subtitle time observer
    subripString = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];
    subripEntries = [SubRipParser parse:subripString];
    if (!subripEntries.count)
        return;
    dispatch_async(dispatch_get_main_queue(), ^{
        _subtitlesTimeObserver = [capturedPlayer addPeriodicTimeObserverForInterval:CMTimeMake(1, 5)
                                                                              queue:_subtitlesQueue
                                                                         usingBlock:^(CMTime time){}];
    });
});

单击按钮时会调用上面的代码。它崩溃了。我是GCD的新手和整个队列的事情,所以也许我很误解,但不应该上面的工作吗?

如果我将主队列上的呼叫更改为同步,那么它可以正常工作。在调用AVPlayer的makePeriodicCall(或类似内容)时,来自subtitleQueue的崩溃。

如果我将定期时间观察器添加到主队列而不是自定义串行队列,则异步调用也可以。但是,文档说添加不同的队列应该没问题。

问题2) 虽然我在这里,但我也有一个关于“捕获”AVPlayer的部分的问题。是否足够安全地捕获变量,还是必须使用__weak并确保它在块内不是NULL?我的情况是包含AVPlayer的控制器是单例,因此它在应用程序的整个生命周期中都存在。我认为这不会使用__weak修饰符。我是否正确地想到这一点?

干杯,谢谢你的帮助!

编辑: 例外是EXC_BAD_ACCESS代码2,因此不应访问的内容是。它发生在运行_subtitlesQueue的单独线程上。它发生在调用[AVPlayerPeriodicCaller _effectiveRateChanged]

我还在_subtitlesQueue上调用外部dispatch_async之前打印出了capturePlayer和_subtitlesQueue(指针值)的值,然后在主队列上调用内部dispatch_async之前,在addPeriodicTimeObserver之前调用主队列上的dispatch_async之前调用。他们都是一样的。

EDIT2: 如果我在subtitleQueue上创建周期性时间观察者周围添加一个同步块,那么事情就可以了......

@synchronized(_subtitlesQueue) {
    _subtitlesTimeObserver = [capturedPlayer addPeriodicTimeObserverForInterval:CMTimeMake(1, 5)
                                                                          queue:_subtitlesQueue
                                                                     usingBlock:subtitleTimeObservedBlock];
}

全部

1 个答案:

答案 0 :(得分:1)

当您向播放AVPlayer添加定期观察者时,-[AVPlayerPeriodicCaller _effectiveRateChanged]中似乎有a bug that causes EXC_BAD_ACCESS。我正在使用的解决方法是:

BOOL playing = player.rate > 0.0f;
if (playing)
{
    [player pause];
}

[player addPeriodicTimeObserverForTimeInterval:myTime queue:mySerialQueue usingBlock:myBlock];

if (playing)
{
    [player play];
}

正如您所指出的,另一种解决方法是传递NULL而不是串行队列,因为这会使主要线程调度队列上的块入队。