0

我在ARC中使用iOS5中的塊來使用AVAudioPlayer播放永不結束的音頻錄製循環。我在收到AVAudioPlayerDidFinishRecording消息後分配一個新的AVAudioPlayer,該消息啓動一個遞歸循環。在播放120個左右音頻剪輯後,我的應用程序一直崩潰。我可以看到它在每個音頻文件加載時佔用越來越多的內存,但從不釋放內存。遞歸塊==內存不足沉默崩潰

我使用的是單個AVAudioPlayer屬性,所以我期待ARC每次分配新玩家時清理舊實例。

  1. 能塊以某種方式導致老AVAudioPlayers留下來?

  2. 使用Observer監視玩家並啓動/分配新玩家會更好嗎?

非常感謝您的幫助!

-(void)playNextAudioItem{ 
    //get ready to load audio doc from iCloud 
    NSURL *ubiquitousContainer = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil]; 
    if(ubiquitousContainer){ 
     //query iCloud 
     query = [[NSMetadataQuery alloc]init]; 
     [query setSearchScopes:[NSArray arrayWithObject:NSMetadataQueryUbiquitousDocumentsScope]]; 
     NSPredicate *pred = [NSPredicate predicateWithFormat:@"(%K == %@)", NSMetadataItemFSNameKey, [NSString stringWithFormat:@"%@.caf", audioItemGUID]]; 
     [query setPredicate:pred]; 
     [[NSNotificationCenter defaultCenter] addObserverForName:NSMetadataQueryDidFinishGatheringNotification object:query queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif){ 
      //iCloud query finished gathering data 
      [query disableUpdates]; 
      [[NSNotificationCenter defaultCenter] removeObserver:self name:NSMetadataQueryDidFinishGatheringNotification object:query]; 
      NSMetadataItem *item1 = [query resultAtIndex:0]; 
      audioCurrentVerse = [[audioDoc alloc] initWithFileURL:[item1 valueForAttribute:NSMetadataItemURLKey]]; 
      //open the audio file 
      [audioCurrentVerse openWithCompletionHandler:^(BOOL success) { 
       if(success){ 
        //configure the session and play the audio file 
        AVAudioSession *audioSession = [AVAudioSession sharedInstance];   
        [audioSession setActive:YES error:nil]; 
        NSError *error = nil; 
        [audioSession setCategory:AVAudioSessionCategoryPlayback error:nil]; 
        audioPlayer = [[AVAudioPlayer alloc] initWithData:audioCurrentVerse.data error:&error]; 
        audioPlayer.numberOfLoops = 0; 
        audioPlayer.delegate = self; 
        if([audioPlayer prepareToPlay]){ 
         [audioPlayer play]; 
        } 
       } 
      }]; 
      [query stopQuery]; 
      query = nil; 
     }]; 
     [query startQuery]; 
    } 
} 

-(void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag{ 
    //get an identifier for the next audio item 
    audioItemIndex++; 
    if(audioItemIndex >= rsAudioItems.recordCount){ 
     verseIndex = 0; 
    } 
    audioItemGUID = [rsAudioItems getRow:audioItemIndex andColumnNamed:@"audioItemGUID"]; 
    //recursive call to play the next audio item 
    NSLog([NSString stringWithFormat:@"%d", playCount++]); 
    [self playNextAudioItem]; 
} 
+5

如果您包含一些相關的代碼,它會更容易有幫助。 –

+1

關於使用嵌套塊的一般規則:不要。內存管理本身就很糟糕。改用while循環。 –

+1

您可能需要花時間創建[SSCCE](http://sscce.org);你可以自己解決這個問題。它肯定會讓別人更容易幫助你。 –

回答

1

塊獲取堆棧的快照,直到塊被釋放才釋放。不要使用遞歸塊(或者真的遞歸,如果它在這種情況下沒有意義),因爲塊永遠不會被釋放,所以快照永遠存在。

使用for循環可以緩解這個問題。您可以將它們添加到串行調度隊列中,然後在執行後釋放它們。

+0

謝謝@JackLawrence!所以,如果我理解正確,我會添加一個串行調度隊列到'while'循環的內部,以便在播放每個音頻文件時阻止工作線程。 AvAudioPlayer依賴於avAudioPlayerDidFinishPlaying回調。在繼續之前,串行隊列是否會等待回調? – bgolson

+0

我已將我的代碼重寫爲使用串行調度隊列,但隊列即時完成。我認爲這是因爲iCloud MetaDataQuery異步運行。 – bgolson

+0

我不太清楚我是否理解你在代碼中所做的一切。這可能是打開DTS(開發人員技術支持)問題並向他們發送您的項目的好處。 –