views:

64

answers:

2

I am trying to debug an application that is throwing up strange (to my untutored eyed) errors. When I try to simply log the count of an array...

NSLog(@"Array has %i items", [[self startingPlayers] count]);

...I sometimes get an error:

-[NSCFString count]: unrecognized selector sent to instance 0x1002af600

or other times

-[NSConcreteNotification count]: unrecognized selector sent to instance 0x1002af600

I am not sending 'count' to any NSString or NSNotification, and this line of code works fine normally.

A Theory...

Although the error varies, the crash happens at predictable times, immediately after I have run through some other code where I'm thinking I might have a memory management issue. Is it possible that the object reference is still pointing to something that is meant to be destroyed? Sorry if my terms are off, but perhaps it's expecting the array at the address it calls 'count' on, but finds another previous object that shouldn't still be there (eg an NSString)? Would this cause the problem?

If so, what is the most efficient way to debug and find out what is that address? Most of my debugging up until now involves inserting NSLogs, so this would be a good opportunity to learn how to use the debugger.

+1  A: 

What does [self startingPlayers] return? Try printing that first:

NSLog("startingPlayers is %@", self.startingPlayers);

Perhaps startingPlayers contains a bad pointer (uninitialized) or a pointer to something that has already been released (and reused for something else).

progrmr
Excellent. Well this time it thought it was something completely different:startingPlayers is <NSKeyValueObservationInfo 0x10018f8e0> (<NSKeyValueObservance 0x114e85470: Observer: 0x114e85390, Key path: isHero, Options: <New: NO, Old: NO, Prior: NO> Context: 0x0, Property: 0x100195940>)Probably I'm releasing the array somewhere I guess.
Ben Packard
Releasing it once too many times OR not retaining it when you should be.
progrmr
+5  A: 

This is a sign that the memory location at which your code is expecting your array to live has either:

  1. Been deallocated and another variable has been allocated in the same place
  2. Been clobbered by some bad code

My bet would be on the first one. You'll want to carefully look at where you are allocating the array and make sure that you're not allowing its retain count to reach zero.

Remember that if you're allocating the array using a convenience method (basically one that starts with array) and not either retaining it or assigning it using dot notation (e.g. self.myArray = [NSArray arrayWith...]) and a property marked retain, it will be freed possibly as soon as the method in which you allocated it returns.

TL;DR is to check where you're assigning the array and make sure you're using something like this:

self.startingPlayers = [NSArray arrayWithObjects:@"first", @"second", nil];

and not like this:

startingPlayers = [NSArray arrayWithObjects:@"first", @"second", nil];

That one's bitten me countless times, including in the middle of a presentation right after I mentioned not to do it.

Frank Schmitt
Very helpful, thanks. One question before I check my code - right before it fails, I'm using if ([self startingPlayers]) to confirm that it exists, and it does. Would this pass though regardless?
Ben Packard
Yes, you can end up with a non-null pointer (e.g. startingPlayers) to an object that the system has deallocated.
Frank Schmitt
One thing you might find useful is the environment variable NSZombieEnabled. If you set that (in Xcode the executable's info sheet lets you do that I think), instead of deallocating an object and returning its memory to the heap, the system will replace it with an instance of NSZombie. Whenever this object receives a message it logs it.
JeremyP
You mean another *object* has been allocated in the same place. The variable is part of the object that owns the (now-dead) array; the object, the array, is what died prematurely.
Peter Hosey