2

Long story short, I am developing a simple game, and I have a Game object that keeps track of the current game state, as well as some additional things that are useful/required later on. One of these is a reference to the UIViewController that loaded the game, which is simply declared as a property on the object:

@interface Game : NSObject {
    //ivars
}

@property (nonatomic, retain) myViewController* viewController;

The getters/setters for this property are generated using @synthesize viewController, and all seems well.

Now there are only two paths through which this property is accessed, when the game is won and when the game is lost. The "game is lost" path works fine. The "game is won" path is causing a bizarre error in which the value of the viewController pointer seems to get overwritten with garbage.

In order to figure out what is going on, I overrode the getters and setters as follows:

- (myViewController*) viewController {
    NSLog(@"Getting");
    return viewController;
}

- (void) setViewController:(myViewController*)controller {
    NSLog(@"Setting");
    viewController = controller;
}

...and set breakpoints inside (I know I'm not retaining the view controller is my setter, but that is not the issue; the view controller remains on the stack and does not actually need to be retained by Game). Here is what I got in GDB:

//first call to setter
po controller
<myViewController: 0x9208130>

//call to getter on the "game is lost" path
(gdb) po viewController
<myViewController: 0x9208130>

//call to setter, starting a new game from the same view controller
(gdb) po controller
<myViewController: 0x9208130>

//call to getter on the "game is lost" path
(gdb) po viewController
0xbea2222c does not appear to point to a valid object.
(gdb) print viewController
$1 = (myViewController *) 0xbea2222c

So at some point, the value of the pointer was changed from 0x9208130 to 0xbea2222c, but I can't figure out where. There is no code that assigns into the viewController property (or its backing ivar) apart from the call to the setter at the start of the game. Yet clearly something is overwriting the value with garbage on the "game is won" path.

Is this possibly a buffer overrun issue? If so what is a good approach for tracking it down?

Edit

I added a second pointer (as an ivar in another class) that I set to the value of game.viewController at the start of play, and using this pointer for the "game is won" path works. So it appears to me that something is trashing the original pointer. Still no idea what, however.

aroth
  • 54,026
  • 20
  • 135
  • 176
  • Garbage collectors move objects to different addresses, but that should not affect your references, which will be unaffected. – Ryan Russell Sep 08 '11 at 00:31
  • @Ryan - I'm not using the automatic garbage collection. And even if it were the case that the garbage collector moved the object and updated the pointer, it should still point to the same object. According to GDB whatever exists at `0xbea2222c` is no longer the same object. Also why would the garbage collector only trigger on the "game is won" path? – aroth Sep 08 '11 at 00:34
  • Sorry for the simpleton comment, but you've done the Leaks instrument profiling thing right? What did that tell you? –  Sep 08 '11 at 00:41
  • Binary search, and find the offending line with log(n) time. :D –  Sep 08 '11 at 00:42
  • @bdares - Wouldn't that require sorting all the lines by suspected degree of erroneousness first? Granted that would only need to be done once, but it would take more than log(n) time to do it. – aroth Sep 09 '11 at 00:17
  • @aroth the code gets executed sequentially, so you already have the order in which it's executed: line by line. Of course, the heuristic of adding debug lines before/after suspect lines would probably help. –  Sep 09 '11 at 00:19

2 Answers2

3

Your setter is wrong. It should read:

- (void) setViewController:(yeticonfettiViewController*)controller {
    NSLog(@"Setting");
    [viewController autorelease];
    viewController = [controller retain];
}

Otherwise you are never retaining the controller and it will likely be garbage collected.

Stefan Arentz
  • 34,311
  • 8
  • 67
  • 88
  • Yep, the symptom fits this perfectly. – Hot Licks Sep 08 '11 at 00:56
  • I was going to mention in my question that this was *not* the issue (same thing happens using the auto-generated setter which performs the retain). The view controller is always retained, because it is still on the stack of active view controllers. The property in the `Game` object doesn't really need to be declared as `retain` in the first place. The problem is definitely not that the `UIViewController` is being released/deallocated too early. That was the very first thing I checked. – aroth Sep 08 '11 at 00:59
1

It does not look like there is enough data to diagnose the problem from the information you have posted.

If you want to further debug this problem, I suggest setting a break point on the address in memory of the pointer ivar. That way you will be notified if it is somehow changed outside of your setter's use.

You can accomplish this by breaking at some point during your Game objects creation and selecting to "Watch Variable" on the Game object's controller ivar.

Alternatively, asksol has a good example of using gdb in a similar fashion external from Xcode in response to another question here.

Community
  • 1
  • 1
paulfi
  • 244
  • 1
  • 2
  • Thanks, this let me solve the problem. It was in fact a buffer overrun issue. Basically I have an array of state information that is indexed by level (one entry for each level). On the "game is won" path, the level gets incremented past the last actual level, and an access into this state array was going out of bounds. Fixed that up and problem solved. – aroth Sep 09 '11 at 00:05