views:

327

answers:

2

Hi,

I have the following method:

-(void)testAPIModule {
 self.requests = [NSMutableArray array];
 NSLog(@"making arrays");
 /*(A)*/ id array1 = [NSArray arrayWithObjects:[NSNumber numberWithInt:1], [NSNumber numberWithFloat:2], nil];
 /*(B)*/ id array2 = [NSArray arrayWithObjects:[NSNumber numberWithInt:4], [NSNumber numberWithInt:5]];
 NSLog(@"made array=%@",array2);

 for( ServerRequest *req in self.requests ) {
  [Networker sendRequest:req withDelegate:self];
  [req release];
 }
}

The code runs as expected.

However, if I comment out line (A) OR remove the ",nil" at the end of it, I get an EXC_BAD_ACCESS error at line (B)! According to the debugger, the error occurs in CFRetain in the +[NSArray arrayWithObjects] built-in constructor.

In addition, if I comment out line (A) and comment out the for(...) loop, the code runs through the method.

This is very unexpected to me. What am I doing wrong on line (B)? And why would creating a completely different array on line (A) let the method run through? And why does commenting out the for(...) loop prevent the error on line (B) that is before it?

Can someone explain why this is? Or at least give me some advice for debugging? I've already verified that the method is only running once and that "self" is valid.

+3  A: 

When using the convenience method arrayWithObjects, you must specify nil as the last element.

Documentation says:

arrayWithObjects:

Creates and returns an array containing the objects in the argument list.

+ (id)arrayWithObjects: (id)firstObj, ... 

Parameters

firstObj, ...
A comma-separated list of objects ending with nil.

Jacob H. Hansen
Well, that's news to me. Thanks. Although I still have no idea why line B worked when line A preceded it. I guess it just filled with garbage that happened to align with an NSArray* pointer?
brainfsck
Probably because the error occurred when you step past line A. Not sure why line B would have worked at all — it should definitely cause a problem as well.
Quinn Taylor
It all depends on what's on the stack at the time. It's very likely that the nil from line A was still around on the stack for line B, so you got lucky.
kperryua
@kperryua I guess that depends on your definition of "lucky"
brainfsck
+1  A: 

Add -Wformat to your Other Warning Flags and the compiler will pick up the missing nil for you.

The second one works after the first because the elements are all in the same position on the stack. So after the return from the first one, the stack still contains a pointer to [NSNumber numberWithInt:1], a pointer to [NSNumber numberWithFloat:2] and nil (and those pointers are even still valid since the autorelease pool hasn't been drained yet!). When you call the second one, without the nil, it replaces the pointers on the stack, but the nil is left unchanged. If your second attempt had had three numbers, it would likely have crashed in the same way, since the third number would have overwritten the nil and then whatever was next would be left.

Peter N Lewis