2

I got an iPhone crash report with a SIGSEGV and I think I've narrowed down the possible cause and a solution. Since crashes caused by threads are hard to debug I can't repro this problem, but could use some help with my hypothesis - is it sound?

My code uses ASIHttpRequest to download a set of files using an ASINetWorkQueue. Here is a simplified sample

//initialize download queue and do this code block in a loop for each file

NSURL *fileURL = [NSURL URLWithString:...
__block ASIHTTPRequest *fileRequest = [ASIHTTPRequest requestWithURL:fileURL];

[fileRequest setCompletionBlock:^{
   //do some stuff   
}];
[fileRequest setFailedBlock:^{
    NSString *someError = [NSString stringWithFormat:...
    [self someErrorMethod:someError];       
}];

[downloadQueue addOperation:...

-(void)someErrorMethod(NSString *errorMessage) {
    DDLogWarn(errorMessage);

    if ([self downloadQueue]) {
        for (ASIHTTPRequest *request in [[self downloadQueue] operations]) {
            [request clearDelegatesAndCancel];
        }
        [[self downloadQueue] reset];
    }
 }

The top 2 lines of the crash report are

  • libobjc.A.dylib 0x31846fbc objc_msgSend + 15
  • MyApp 0x0002cab5 -[Myapp someErrorMethod:] (MyApp.m:)

My thinking for why this happened

  • A file download fails and the failed block is called
  • It goes through every request and clears delegates and cancels them and then resets the queue
  • However, while it is till running, another file download fails and enters the failed block callback
  • However, since it has now been cancelled, its failed block has been released
  • When the code tries to log the error message, its memory has been released and unpredictable results follow

Does this make sense? Since I am newish to Objective-C, is my analysis correct or am I missing something obvious?

I am thinking of using a lock to make the errorMethod thread safe with the hope that it will fix this issue. Does that sound like the right solution based on the code above?

Thanks

suhail
  • 63
  • 5
  • 1
    Not sure about your error, but you may want to check this out: http://stackoverflow.com/a/5023583/330494 You should use blocksafeSelf to reference self withing an ivar block. – Barlow Tucker Feb 20 '12 at 16:35
  • Thanks Barlow I am using a singleton class here that never gets deallocated so I dont think a retain cycle is a problem, right? – suhail Feb 23 '12 at 03:09

1 Answers1

4

This doesn't sound likely. ASIHttpRequest likely performs all of its callbacks on the same thread (I'm fairly certain on this one).

If I had to guess, your error is more likely in this line:

DDLogWarn(errorMessage);

The first parameter to DDLogWarn is a format, not a string. This will likely crash in any case that errorMessage includes a %. What you meant is:

DDLogWarn(@"%@", errorMessage);

Since DDLogWarn() is a varags method, it will start substituting the (random) values it finds on the stack for any % substitutions in the string. It will read the stack until you run out of % substitutions. If any of the % substitutions are pointer-based (like %s or %@), then it will follow the pointer to a random location.

SEG_ACCERR means that you've requested a piece of memory you don't own. SEG_MAPERR means you've requested a piece of memory that is not mapped. Either is an expected result of following a totally random pointer.

Rob Napier
  • 286,113
  • 34
  • 456
  • 610
  • Thanks for the response Rob. That is indeed something foolish I missed. However, would that cause a SEGV_ACCERR? I tried to repro with a % in the string and got a SEGV_MAPERR instead libobjc.A.dylib 0x0262509b objc_msgSend + 15 CoreFoundation 0x0245f6f8 __CFStringAppendFormatCore CoreFoundation 0x023aab6c _CFStringCreateWithFormatAndArgumentsAux Foundation 0x0137f1a5 -[NSPlaceholderString initWithFormat:locale:arguments:] Foundation 0x013cc66e -[NSString initWithFormat:arguments:] MyApp 0x00062867 +[DDLog log:level:flag:context:file:function:line:format:] – suhail Feb 22 '12 at 18:05
  • Updated; either form of SEGV would be expected. – Rob Napier Feb 22 '12 at 20:10