views:

50

answers:

2

Hi there, I'm just starting out on my first 'real' cocoa app. Note that this is Mac, not iPhone.

This function is called when my async download of onemanga finishes, and is intended to parse an NSArray list of manga from it. I get a NSArray from nodesForXPath, and retain it to make sure it's mine. (Also tried retaining it twice, heh). Unfortunately, trying to retrieve the count of the NSArray causes an EXC_BAD_ACCESS. The point of crashing is marked by two comments below.

- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSLog(@"Download Succeeded! Received %d bytes of data. Beginning Parse.",[mangaListData length]);
NSString* theQuery = [[NSString alloc]initWithString:@"//tr/td[@class=\"ch-subject\"]/text()"];
NSError *XMLError=nil;
NSError *XPathError=nil;
NSString* mangaListHTML;
NSString* fixedMangaListHTML;
mangaListHTML = [[NSString alloc] initWithData:mangaListData encoding:NSUTF8StringEncoding];
fixedMangaListHTML = [mangaListHTML stringByReplacingOccurrencesOfString:@" & " withString:@" & "];
NSXMLDocument* xmlDoc = [[NSXMLDocument alloc] initWithXMLString:fixedMangaListHTML
                                                        options:(NSXMLNodePreserveWhitespace|NSXMLNodePreserveCDATA)
                                                        error:&XMLError];
if (XMLError) {
    NSLog(@"XML Parse error: %@", XMLError);
    return;
};
[fixedMangaListHTML release];
[mangaListHTML release];
NSArray* results = [[xmlDoc nodesForXPath:theQuery error:&XPathError] retain];
if (XMLError) {
    NSLog(@"Parse error: %@", XPathError);
    return;
};
NSLog(@"Parsing complete. Manga List = ");
//CRASH HAPPENS HERE
NSLog(@"Size of array: %@", [results count]);
//CRASH HAPPENS ABOVE
for(NSXMLNode* title in results){
    NSLog(@"%@\n", title.description);
};
[XMLError release];
[XPathError release];
[connection release];
[mangaListData release];

}

Heres the output of where in gdb. (I don't really know how to use gdb yet, so any commands I could run to get more info here would be highly appreciated.)

#0  0x00007fff855691d1 in objc_msgSend_vtable5 ()
#1  0x00007fff87e97207 in _NSDescriptionWithLocaleFunc ()
#2  0x00007fff8509ba2d in _CFStringAppendFormatAndArgumentsAux ()
#3  0x00007fff8509aead in _CFStringCreateWithFormatAndArgumentsAux ()
#4  0x00007fff85119f5f in _CFLogvEx ()
#5  0x00007fff87ef937f in NSLogv ()
#6  0x00007fff87ef9317 in NSLog ()
#7  0x0000000100002bca in -[TMMoneManga connectionDidFinishLoading:] (self=0x1001999f0, _cmd=0x7fff8803b5de, connection=0x1001689a0) at /Users/ripdog/Documents/TheMangaMachine/TMMoneManga.m:120
#8  0x00007fff87f16b8c in _NSURLConnectionDidFinishLoading ()
#9  0x00007fff8063f18e in URLConnectionClient::_clientDidFinishLoading ()
#10 0x00007fff806a4502 in URLConnectionClient::ClientConnectionEventQueue::processAllEventsAndConsumePayload ()
#11 0x00007fff8062b8fb in URLConnectionClient::processEvents ()
#12 0x00007fff8062b6d8 in MultiplexerSource::perform ()
#13 0x00007fff850b6f21 in __CFRunLoopDoSources0 ()
#14 0x00007fff850b5119 in __CFRunLoopRun ()
#15 0x00007fff850b48df in CFRunLoopRunSpecific ()
#16 0x00007fff80b83ada in RunCurrentEventLoopInMode ()
#17 0x00007fff80b838df in ReceiveNextEventCommon ()
#18 0x00007fff80b83798 in BlockUntilNextEventMatchingListInMode ()
#19 0x00007fff8845fa2a in _DPSNextEvent ()
#20 0x00007fff8845f379 in -[NSApplication nextEventMatchingMask:untilDate:inMode:dequeue:] ()
#21 0x00007fff8842505b in -[NSApplication run] ()
#22 0x00007fff8841dd7c in NSApplicationMain ()
#23 0x00000001000017cd in main (argc=1, argv=0x7fff5fbff6c0) at /Users/ripdog/Documents/TheMangaMachine/main.m:13

Thanks a lot in advance.

+1  A: 

count returns an integer, not an object. Print it with %d not %@.

(If you do the latter, you implicitly convert the integer to a pointer to a non-existent object in the wilds of who knows where, and then invoke its description method.)

walkytalky
That's fascinating, thanks a lot for your response.
Ripdog
+3  A: 

The problem is with your format string. The %@ specifier will display an Objective-C object, but [result count] is an integer. The correct format specifier for an integer is %d. For reference, here is a complete list of Objective-C string format specifiers.

Your question indicated that the crash happens when you try to retrieve [result count]. However if you look at the call stack, your code is frame #7, and the next function on the stack (frame #6) is NSLog. So this tells you the problem is probably something to do with the call to NSLog, and not [result count]. [result count] will already have returned before control flow enters NSLog.

There are various techniques for debugging with gdb. Since you are debugging a crash, your focus will mostly be on examining the call stack, variables and memory (as opposed to stepping through your code as it executes.) So for this kind of debugging, these commands are essential:

  • up This moves the "focus" of the debugger up one frame of the call stack. Let's say you want to examine the variables in your program. You can't see them in objc_msgSend_vtable5 which is where the debugger is stopped. So use up 7 to jump up seven frames so you're looking at your code.
  • down Is the opposite of up. Oftentimes you want to look at what led to disaster, so jumping up to your code and then going down, down, down is a way to get an understanding of how disaster unfolded. (This isn't moving you forward and backward in time, of course.)
  • where Shows you part of the call stack. You already discovered this. My only tip is that you often only care about what's on the top of the call stack. You can use where n to print the first n frames of the call stack.
  • list Shows you the source code of the "focussed" frame, with an arrow indicating the execution point. So, for example, up 7 followed by list should show you the source of connectionDidFinishLoading with an indicator next to the call to NSLog. Using list is often more convenient than finding the code in XCode, if you just need to see a few lines of the surrounding context.
  • print Evaluates an expression and prints it. For example, you could do print [results count] to see what [results count] evaluates to. When the debugger is stopped with your program is in a bad state, print can act funny (it can really run code.) So often simple expressions work better.
  • info Info can tell you lots of things, but info locals is really valuable. It shows you the local variables that are in scope in the "focussed" frame of the debugger.
Dominic Cooney
Thank you very much for your help! The book I had been reading to get started with Cocoa implied that %@ was the 'catch-all' format symbol where the compiler did some pythonesque magic and converted it to whatever format symbol was necessary.
Ripdog
My pleasure. `%@` is a catch-all for Objective-C *objects*, and the magic is that it sends it a `descriptionWithLocale` message, if it responds to `descriptionWithLocale`; and failing that a `description` message (as @walkytalky notes in their answer.) And you can see that it gets as far as calling `_NSDescriptionWithLocaleFunc`, which I guess is a helper method in the Objective-C runtime, before crashing.
Dominic Cooney