Mon application dispose d'un rappel d'enregistrement audio appelé par l'infrastructure RemoteIO AudioUnit. L'AudioUnit appelle le rappel dans un thread différent du thread principal, il n'a donc pas de pool de libération automatique. Dans ce rappel, je fais ce qui suit:Fuite de mémoire dans le fil de rappel audio (iOS)
- Alloc un tampon pour les échantillons enregistrés. Pour appeler ce tampon, appelez
AudioUnitRender
. - Ouvrez un fichier pour la consignation à l'aide de
freopen
. - Appelez une méthode de traitement audio.
- En fonction de l'audio, mettez à jour l'interface utilisateur du thread principal en utilisant
performSelectorOnMainThread
(envoyé au contrôleur de vue).
En outre, j'ai une fonction appelée testFilter()
qui fait une analyse comparative, que j'appelle à viewDidLoad
du contrôleur de vue, avant la session audio est initialisé, donc avant le rappel audio est appelé pour la première fois. Dans cette fonction, j'alloue un buffer (en utilisant malloc/free) et appelle la même méthode de traitement audio que j'ai mentionnée ci-dessus.
Maintenant, le problème est (sur l'appareil, et non le simulateur):
- Si je commente l'appel à
testFilter()
, je ne reçois pas de messages associés-fuites de mémoire. - Si je n'appelle
testFilter()
, je commence à avoir un tas de messages à partir de la fonction de rappel audio (Les 5 premiers messages sont mes journaux de la méthodetestFilter()
):
2011-01-20 23: 05: 10,358 TimeKeeper [389: 307] tampon d'initialisation ...
2011-01-20 23: 05: 10,693 TimeKeeper [389: 307] fait ...
2011-01-20 23:05 : 10.696 TimeKeeper [389: 307] tampon de traitement ...
2011-01-20 23: 05: 15,772 TimeKeeper [389: 307] fait ...
2011-01-20 23: 05: 15,775 TimeKeeper [389: 307] temps écoulé 5,073843
2011-01-20 23: 05: 16,319 TimeKeeper [389: 660F] * __NSAutoreleaseNoPool(): Object 0x137330 de __NSCFData de classe autoreleased sans piscine en place - juste une fuite
2011-01-20 23:05: 16.327 TimeKeeper [389: 660f] * __NSAutoreleaseNoPool(): Objet 0x1373a0 de la classe __NSCFData auto-libéré sans pool en place - juste fuite
et ainsi de suite. Le rappel a un fil différent, comme on peut le voir dans le journal.
Pourquoi ces avertissements ne se produisent-ils que si j'appelle une fonction appelée et terminée avant même l'initialisation de la session audio? Comment puis-je détecter la fuite?
Annexe
Les méthodes pertinentes:
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];
}
Et:
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;
}
Ma conclusion finale de tout ce mal de tête n'utilise pas 'freopen' et' NSLog' du tout, mais pour implémenter ma propre classe de journalisation qui utilisera la sortie de fichier ('fprintf' de C ou une classe IO de fichier Cocoa) –