我有一个小项目,它从远程服务器读取 HTTP 流,对其进行解复用,提取音频流,将其解码为 16 位 PCM,然后输入相应的 AudioQueue。解码器/解复用器/提取器在单独的线程中运行,它使用我自己开发的阻塞队列(参见下面的代码)将解码的帧传递给 AudioQueue 回调。队列使用 NSMutableArray 来存储对象。
一旦这个东西在运行中,它就会泄漏插入到队列中的对象。内存分析器说 RefCt 在我期望它为 0 并由 ARC 释放时为 2。
以下是队列/出队方法:
- (id) dequeue {
dispatch_semaphore_wait(objectsReady, DISPATCH_TIME_FOREVER);
[lock lock];
id anObject = [queue objectAtIndex:0];
[queue removeObjectAtIndex:0];
[lock unlock];
dispatch_semaphore_signal(freeSlots);
return anObject;
}
- (void) enqueue:(id)element {
dispatch_semaphore_wait(freeSlots, DISPATCH_TIME_FOREVER);
[lock lock];
[queue addObject:element];
[lock unlock];
dispatch_semaphore_signal(objectsReady);
}
生产者线程这样做:
[pAudioFrameQueue enqueue:[self convertAVFrameAudioToPcm:audioFrame]];
“convertAVFrameAudioToPcm”方法如下所示:
- (NSData*) convertAVFrameAudioToPcm:(AVFrame*)frame {
NSData* ret = nil;
int16_t* outputBuffer = malloc(outputByteLen);
// decode into outputBuffer and other stuff
ret = [NSData dataWithBytes:outputBuffer length:outputByteLen];
free(outputBuffer);
return ret;
}
消费者这样做:
- (void) fillAvailableAppleAudioBuffer:(AudioQueueBufferRef)bufferToFill {
@autoreleasepool {
NSData* nextAudioBuffer = [pAudioFrameQueue dequeue];
if (nextAudioBuffer != nil) {
[nextAudioBuffer getBytes:bufferToFill->mAudioData]; // I know this is not safe
bufferToFill->mAudioDataByteSize = nextAudioBuffer.length;
} else {
NSLog(@"ERR: End of stream...");
}
}
}
在我看来,当 fillAvailableAppleAudioBuffer 退出时,RefCt 应该变为 0,但显然 ARC 不同意并且不释放该对象。
我的简单队列代码中有错误吗?
还是我以错误的方式实例化 NSData?
还是我错过了一些关于 ARC 如何在线程之间工作的特殊规则?顺便说一句,生产者线程是这样开始的:
- (BOOL) startFrameFetcher {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,
(unsigned long)NULL),
^(void) {
[self frameFetcherThread];
});
return YES;
}
任何提示将不胜感激!
PS:最后但并非最不重要的一点是,我确实有另一个相同阻塞队列的实例,它存储我通过 NSTimer 出列和显示的视频帧。视频帧不泄漏!我猜这可能与线程有关。否则,我会期望在两个队列中都看到泄漏。