【问题标题】:Memory problems with [AVAssetWriterInput requestMediaDataWhenReadyOnQueue:usingBlock:][AVAssetWriterInput requestMediaDataWhenReadyOnQueue:usingBlock:] 的内存问题
【发布时间】:2025-12-07 16:05:02
【问题描述】:

我正在编写一个库来使用 AVFoundation 将资产导出到文件。我创建了一个读取器、一个写入器,将输入和输出连接到它们,然后在输入上调用 requestMediaDataWhenReadyOnQueue 方法以开始提取数据。提供给此方法的块回调看起来有点像这样:

[input requestMediaDataWhenReadyOnQueue:queue usingBlock:^{
    while ([input isReadyForMoreMediaData]) {
        CMSampleBufferRef buffer;
        // The track has some more data for us
        if ([reader status] == AVAssetReaderStatusReading
               && (buffer = [output copyNextSampleBuffer])) {
            BOOL result = [input appendSampleBuffer:buffer];
            CFRelease(buffer);
            if (!result) {
                // handle error
                break;
            }
        // The track is finished, for whatever reason
        } else {
            [input markAsFinished]; ⬅
            switch ([reader status]) {
                // inspect the status and act accordingly
            }
        }
    }
}];

这在 iOS 5 上完美运行,但在 iOS 4 上,代码在标有 ⬅ 箭头的行之后死于 EXC_BAD_ACCESS。经过一番摸索后,我觉得在将输入标记为完成后立即以某种方式破坏了该块。在执行坏行之前完全有效的self 指针不知何故变成了0xfff… 或调试器报告的某个垃圾值。但是之前指向它的对象很好,正如僵尸工具所证实的那样,它没有被释放。

我错过了什么?

【问题讨论】:

  • 你解决了这个问题吗?

标签: objective-c ios memory-management avfoundation


【解决方案1】:

看到相同(相似)的问题。 iOS5开心,iOS4.3.5,不开心。有兴趣了解您最终会发现什么。

通过在 requestMedatWhenReadyOnQueue 块之前显式保留写入器、写入器输入、读取器、读取器输出并在 else 子句的最后显式释放所有四个来解决此问题。

文档确实说在标记完成后,“该块应该退出。”也许他们不是在开玩笑。如果您执行退出以外的任何操作,则这是一个错误。不过,上述解决方法似乎可行。

更新:我仍然发现即使在保留和释放所有资产对象之后它偶尔也会崩溃。正如您的问题所观察到的,在您将编写器输入标记为完成后不久它就会崩溃,就好像块本身正在被释放一样。而不仅仅是将块作为函数的一部分传递。我创建了一个复制的块属性,它是长寿命对象的一部分。我用 Block_copy 初始化它,并且只在长寿命对象的析构函数中释放它。这似乎可以解决问题。从那以后我没有看到任何 4.3.5 崩溃。

【讨论】:

  • 你能发布你的代码来解决这个问题吗?有同样的问题。
【解决方案2】:

尝试将 [self retain] 作为块的第一行,将 [self release] 作为最后一行。

另一个关键问题是,如果使用 requestMediaDataWhenReadyOnQueue 暂停应用程序(进入后台),您需要明确覆盖所有 [reader status] 值,因为它会在应用程序重新启动时失败。在某些情况下,我发现该块多次运行并带有失败状态标志。在其他具有类似代码的帖子中,有很多 [retain] AV 变量,然后在块的末尾释放。因为该块可以多次运行,所以这种方法在应用程序进入后台状态时不起作用。

我发现以下在“开关”(上图)中运行良好:

                case AVAssetReaderStatusReading:
                    break;

                case AVAssetReaderStatusCompleted:
                    [videoWriterInput markAsFinished];
                    //do something else, like add an audio stream
                    [videoWriter finishWriting];
                    break;

                case AVAssetReaderStatusFailed:
                    [videoWriterInput markAsFinished];
                    [videoWriter finishWriting];
                    break;

                case AVAssetReaderStatusCancelled:
                case AVAssetReaderStatusUnknown:
                    [videoWriterInput markAsFinished];
                    [videoWriter cancelWriting];
                    break;
            }

            dispatch_sync(dispatch_get_main_queue(), ^{
              //hide any progress indicators
            });

            break;

除了“self”,什么都没有保留。如果需要,该块应自动保留变量。

【讨论】: