2011-01-20 23 views
0

我的應用程序有一個由RemoteIO AudioUnit框架調用的音頻記錄回調。 AudioUnit在與主線程不同的線程中調用回調,因此它沒有自動釋放池。在此回調中,我執行以下操作:音頻回調線程(iOS)中的內存泄漏

  • 分配記錄樣本的緩衝區。
  • 請致電AudioUnitRender填寫此緩衝區。
  • 使用freopen打開文件進行記錄。
  • 調用音頻處理方法。
  • 根據音頻,使用performSelectorOnMainThread(發送到視圖控制器)更新主線程上的UI。

另外,我有一個名爲testFilter()功能,做一些基準測試,這是我在視圖控制器的viewDidLoad調用,音頻會話初始化之前,因此之前的音頻調用回調函數的第一次。在這個函數中,我分配一個緩衝區(使用malloc/free)並調用上面提到的相同的音頻處理方法。現在

,問題是(設備,而不是模擬器上):

  • 如果我註釋掉調用testFilter(),我沒有得到任何內存泄漏相關的消息。
  • 如果我不叫testFilter(),我開始越來越一堆消息從音頻回調中(第5條消息是從testFilter()方法我的日誌):

2011-01-20 23: 05:10.358 TimeKeeper的[389:307]初始化緩衝器...

2011-01-20 23:05:10.693 TimeKeeper的[389:307]進行...

2011-01-20 23:05 :10.696 TimeKeeper [389:307]處理緩衝區...

2011-01-20 23:05:15.772 TimeKeeper的[389:307]完成了...

2011-01-20 23:05:15.775 TimeKeeper的[389:307]經過時間5.073843

2011-01-20 23:05:16.319 TimeKeeper的[389:660F] * __NSAutoreleaseNoPool():有沒有到位池自動釋放類__NSCFData的對象0x137330 - 剛剛泄露

2011-01-20 23:05: 16.327 TimeKeeper [389:660f] * __NSAutoreleaseNoPool():類__NSCFData的對象0x1373a0自動釋放,不存在池 - 僅泄漏

等等。回調具有不同的線程,如日誌中所示。

爲什麼只有在音頻會話初始化之前調用並完成的函數纔會發生這些警告?我如何檢測泄漏?

附錄

相關的方法:

void testFilter() { 
#if TARGET_IPHONE_SIMULATOR == 0 
    freopen([@"/tmp/console.log" cStringUsingEncoding:NSASCIIStringEncoding],"a",stderr); 
#endif 
    int bufSize = 2048; 
    int numsec=100; 
    OnsetDetector * onsetDetector = [[OnsetDetector alloc] init]; 
    AudioSampleDataType *buffer = (AudioSampleDataType*) malloc (44100*numsec * sizeof(AudioSampleDataType)); // numsec seconds of audio @44100 

    AudioBufferList bufferList; 
    bufferList.mNumberBuffers = 1; 
    bufferList.mBuffers[0].mData = buffer; 
    bufferList.mBuffers[0].mDataByteSize = sizeof (AudioSampleDataType) * bufSize; 
    bufferList.mBuffers[0].mNumberChannels = 1; 

    //--- init buffer 
    NSLog(@"\n\n---***---***---"); 
    NSLog(@"initializing buffer..."); 
    for (int i = 0; i < 44100*numsec; ++i) { 
     *(buffer+i) = (AudioSampleDataType)rand(); 
    } 
    NSLog(@"done..."); 

    NSLog(@"processing buffer..."); 
    CFAbsoluteTime t0 = CFAbsoluteTimeGetCurrent(); 
    for (int i = 0; (i+1)*bufSize < 44100*numsec; ++i) { 
     bufferList.mBuffers[0].mData = buffer + i * bufSize; 
     [onsetDetector process:&bufferList]; 
    } 
    CFAbsoluteTime t1 = CFAbsoluteTimeGetCurrent(); 
    NSLog(@"done..."); 
    NSLog(@"elapsed time %1.6f",(double)(t1-t0)); 
    free(buffer); 
    [onsetDetector release]; 
} 

和:

OSStatus recordingCallback (void *inRefCon, 
          AudioUnitRenderActionFlags *ioActionFlags, 
          const AudioTimeStamp *inTimeStamp, 
          UInt32 inBusNumber, 
          UInt32 inNumberFrames, 
          AudioBufferList *ioData) { 

    AudioBufferList bufferList; 

    // redundant 
    SInt16 *buffer = (SInt16 *)malloc (sizeof (AudioSampleDataType) * inNumberFrames); 
    bufferList.mNumberBuffers = 1; 
    bufferList.mBuffers[0].mData = buffer; 
    bufferList.mBuffers[0].mDataByteSize = sizeof (AudioSampleDataType) * inNumberFrames; 
    bufferList.mBuffers[0].mNumberChannels = 1; 

    ioData = &bufferList; 

    // Obtain recorded samples 
    OSStatus status; 

    MainViewController *mainViewController = (MainViewController *)inRefCon; 
    AudioManager  *audioManager  = [mainViewController audioManager]; 
    SampleManager  *sampleManager  = [audioManager sampleManager]; 

    status = AudioUnitRender([audioManager audioUnit], 
          ioActionFlags, 
          inTimeStamp, 
          inBusNumber, //1 
          inNumberFrames, 
          ioData); 

#if TARGET_IPHONE_SIMULATOR == 0 
    freopen([@"/tmp/console.log" cStringUsingEncoding:NSASCIIStringEncoding],"a",stdout); 
#endif 

    // send to onset detector 
    SInt32 onset = [[audioManager onsetDetector] process:ioData]; 
    if (onset > 0) { 
     NSLog(@"onset - %ld\n", sampleManager.recCnt + onset); 

     //--- updating the UI - must be done on main thread 
     [mainViewController performSelectorOnMainThread:@selector(tapButtonPressed) withObject:nil waitUntilDone:NO]; 
     [mainViewController performSelectorOnMainThread:@selector(onsetLedOn) withObject:nil waitUntilDone:NO]; 
    } 

    sampleManager.recCnt += inNumberFrames; 

    free(buffer); 
    return noErr; 
} 

回答

0

看起來問題是由freopen引起的,之後是fclose。我將stderr作爲第三個參數傳遞給freopen,因此日誌消息被重定向到一個文件。如果在回調結束時撥打fclose,則不會出現內存泄漏錯誤。

有些問題仍需要雖然一個答案:

  • 爲什麼使用autorelease池並沒有解決這個問題?我想這是因爲freopen是一個C功能,所以沒有autorelease消息發送無論如何。
  • 的唯一的情況,其中的錯誤消息被投擲是:
    • freopen在主線程上的功能時,無需fclose
    • freopen中在不同的線程的音頻回調使用,無需fclose

也就是說,如果freopen僅用於音頻回調線程(而不是在主線程),不會出現任何錯誤。

編輯

另一個奇怪的現象是,即使做兩個線程使用fclose,但我通過stdout在主線程和stderr音頻回調線,我得到的錯誤消息。我認爲當有多於一個線程時,調用NSLog的方法會有所幫助。對此的任何見解都是受歡迎的。

2

「公正泄漏」 的錯誤信息就是你得到的地方你的方法(或其他方法您的方法調用)嘗試autorelease一個對象,當沒有autorelease池。沒有游泳池,沒有任何東西可以跟蹤哪些物體需要釋放,因此它們只是泄漏。

在主線程上,會爲您自動創建和管理這樣的池,但在輔助線程上,您必須自己執行此操作。

你需要做的是改變你的功能分配池:

void myFunction() { 
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 

    // do stuff here 

    [pool release]; 
} 

,這將會使該錯誤信息消失,並修復泄漏。

+0

謝謝。我知道autorelease池,我知道它會使錯誤消息消失,但我不明白兩件事:1.什麼對象正在嘗試autorelease,以及2.爲什麼這些消息只出現在我調用一個函數主線程,甚至在音頻線程創建之前。 – 2011-01-21 12:28:40

+0

**更新**即使我將所有回調命令放入自動釋放池(並在末尾調用[池漏]),仍會出現錯誤消息。任何其他想法? – 2011-01-21 12:50:53