user149533
user149533

Reputation:

Unrecognised selector crash within [NSDictionary valueForKey:]

I'm occasionally getting this crash, which is very hard to reproduce:

0   CoreFoundation                  0x39ff73e2 __exceptionPreprocess + 158
1   libobjc.A.dylib                 0x3905095e objc_exception_throw + 26
2   CoreFoundation                  0x39ffaf2c -[NSObject(NSObject) doesNotRecognizeSelector:] + 180
3   CoreFoundation                  0x39ff9648 ___forwarding___ + 388
4   CoreFoundation                  0x39f51204 _CF_forwarding_prep_0 + 20
5   Foundation                      0x32914bec -[NSDictionary(NSKeyValueCoding) valueForKey:] + 28
6   MyAppName                       0x00032112 -[myViewController stopPlayersWithVolume:] (myViewController.m:151)

From this code:

- (void)stopPlayersWithVolume:(double)volume
{
    for (NSString *file in players)
    {
        AVAudioPlayer *player = [players valueForKey:file];
        if (player.volume == volume || volume == 0)
            [player stop];
    }
}

players is an NSMutableDictionary property, accessed without self. because I don't believe it's needed with ARC. The keys are filenames (e.g. "mysound.mp3") and the values are AVAudioPlayer objects.

Is the stack trace saying that the parameter file I'm passing to [NSMutableDictionary valueForKey] is not actually an NSString, and hence the unrecognised selector? It is always an NSString in the debugger, as you'd expect as it comes from a fast enumeration through the dictionary, but the crash never occurs in the debugger.

My only thought is that there's a threading issue corrupting the dictionary. The code above is being fired by an NSTimer but that's just a message via the run loop, not a separate thread, so I believe there should be no need to worry about cross-thread access?

Any suggestions appreciated. Sorry to just dump this out but I'm stuck.

Edit:

The players dictionary is allocated in viewDidLoad thus:

players = [[NSMutableDictionary alloc] init];

And declared as follows:

@property (retain, nonatomic) NSMutableDictionary *players;

And synthesised as follows:

@synthesize players;

Upvotes: 1

Views: 875

Answers (2)

user149533
user149533

Reputation:

The code to allocate the dictionary (and set up the audio stuff) looked like this:

NSError *error = [[NSError alloc] init];
NSArray *names = [fm contentsOfDirectoryAtPath:resourcePath error:&error];
players = [[NSMutableDictionary alloc] init];
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryAmbient error:&error];
[[AVAudioSession sharedInstance] setActive:YES error:&error];

I must have copied and pasted it from somewhere, because that use of NSError is wrong, as far as I know, though hardly likely to cause a problem. I changed the code to this:

NSArray *names = [fm contentsOfDirectoryAtPath:resourcePath error:nil];
players = [[NSMutableDictionary alloc] init];
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryAmbient error:nil];
[[AVAudioSession sharedInstance] setActive:YES error:nil];

And the problem goes away, it appears. Can't be 100% sure but I can reproduce the crash within 10 attempts with the former code and never with the latter. No idea why.

Upvotes: 0

Jesse Rusak
Jesse Rusak

Reputation: 57168

It's likely that your players dictionary or its contents are being deallocated while you're iterating through them. Can you turn on NSZombies and try to reproduce the crash in the debugger? It will give you more information about what object is being deallocated.

Upvotes: 0

Related Questions