I’m trying to track down a freeze problem with an app I am developing. I may be in the deep end of the NSAutoreleasePool and am screwing things up.
The app is playing a midi file. If I comment out the “simRespondToFileNote” code below that uses an NSAutoreleasePool it doesn’t freeze. If I let the code run, it will freeze at seemingly random points The crash log/console output doesn’t seem to indicate where the issue is occurring.
Here is the program flow:
-
I’m using the Bass midi lib (C lib); it plays the midi file in its own thread.
-
When a midi event occurs, a callback is triggered and I wrap the midi event data in an NSDictionary and route it to the main thread so I can do UI updates and some other stuff.
Here is the code that does the routing:
- (void)forwardFileNoteIn:(int) note withVelocity: (int) velocity
{
int position = BASS_ChannelGetPosition(midiFileStream, BASS_POS_MIDI_TICK);
float percent = ((float)position / (float)totalTicks);
int ticksInLoop = outLoopTick - inLoopTick;
QWORD bytes=BASS_ChannelGetPosition(midiFileStream, BASS_POS_BYTE); // get position in bytes
double seconds=BASS_ChannelBytes2Seconds(midiFileStream, bytes); // translate to seconds
int timeStamp =seconds*1000; // translate to milliseconds
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSDictionary *midiData = [NSDictionary dictionaryWithObjectsAndKeys:
@"fileNoteIn", @"eventType",
[NSNumber numberWithInt:note], @"note",
[NSNumber numberWithInt:velocity],@"velocity",
[NSNumber numberWithInt:timeStamp],@"timeStamp",
[NSNumber numberWithInt:position],@"position",
[NSNumber numberWithFloat:percent],@"percentPlayed",
[NSNumber numberWithInt:ticksInLoop],@"ticksInLoop",
nil];
[delegate performSelectorOnMainThread:@selector(midiFileEvent:)
withObject:midiData
waitUntilDone:NO];
[pool release];
}
- From the delegate a message is sent to another object using the NSDictionary instance as a param. That object either sends the NSDictionary instance immediately to another object or queues it to be sent after a short delay (using performSelector: afterDelay: ).
Is it possible that the NSAutoreleasePool is deleting the NSDictionary instance before the queued message is triggered? I am not draining the pool anywhere – should I be doing that?
- (void)simRespondToFileNote:(NSDictionary *)dictionary
{
int velocity = [[dictionary objectForKey:@"velocity"] intValue];
if (velocity == 0){
// noteOff - send it through
[delegate routeUserSimMidiEvent:dictionary];
} else {
float totalPercentCorrect = [dataSource getUserCorrectPercent];
int note = [[dictionary objectForKey:@"note"] intValue];
if (totalPercentCorrect < _userAccuracy){
float lateNoteOnTimeDelay = (dataSource.postTimeHighAccuracy - (dataSource.postTimeHighAccuracy /4)) / 1000.;
float lateNoteOffTimeDelay = lateNoteOnTimeDelay + .1; // revise
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// create noteOff data w/ velocity == 0; timeStamp == 0;
NSDictionary *midiData = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithInt:note], @"note",
[NSNumber numberWithInt:0],@"velocity",
[NSNumber numberWithUnsignedInt:0],@"timeStamp",
nil];
[self performSelector:@selector(simLateResponseToFileNote:) withObject:dictionary afterDelay: lateNoteOnTimeDelay];
[self performSelector:@selector(simLateResponseToFileNote:) withObject:midiData afterDelay: lateNoteOffTimeDelay];
[pool release];
} else {
float lateNoteOnTimeDelay = (dataSource.postTimeLowAccuracy + (dataSource.postTimeLowAccuracy /4)) / 1000.0;
float lateNoteOffTimeDelay = lateNoteOnTimeDelay + .1; // revise
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// create noteOff data w/ velocity == 0; timeStamp == 0;
NSDictionary *midiData =[NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithInt:note], @"note",
[NSNumber numberWithInt:0],@"velocity",
nil];
// queue late noteOn
[self performSelector:@selector(simLateResponseToFileNote:) withObject:dictionary afterDelay: lateNoteOnTimeDelay];
// queue late noteOff
[self performSelector:@selector(simLateResponseToFileNote:) withObject:midiData afterDelay: lateNoteOffTimeDelay];
[pool release];
}
}
}
Your creation of a temporary autorelease pool in
simRespondToFileNote:isn’t very useful, but shouldn’t be a problem. Your call toperformSelector:withObject:afterDelay:will retaindictionaryuntil its called. IfforwardFileNoteIn:withVelocity:is on a thread that doesn’t have an autorelease pool yet, you may have to create it, but generally you do that at the top of the method. If this thread already has a pool, there’s no reason to create on here.From your description, I would suspect that
simLateResponseToFileNote:is blocking the main thread too long. I would look there for a bottleneck.