3

I am receiving EXC_BAD_ACCESS signal in my iOS application when doing a recursion that involves objective-c blocks. Here is the simplified code:

- (void)problematicMethod:(FriendInfo*)friendInfo onComplete:(void(^)(NSString*))onComplete1 {

[self doSomethingWithFriend:friendInfo onComplete:^(Response* response) {
    switch (response.status) {
        case IS_OK:
            onComplete1(message);
            break;

        case ISNT_OK:
            // Recursively calls the method until a different response is received 
            [self problematicMethod:friendInfo onComplete:onComplete1];
            break;          

        default:
            break;
    }
}];
}

So basically, the problematicMethod, in this simplified version, calls doSomethingWithFriend:onComplete:. When that method finishes (onComplete), and if everything was ok, the original onComplete1 block gets called, and this works fine.

But if something went wrong, problematicMethod needs to be called again (the recursion part), and when this happens for the first time, I immediately get EXC_BAD_ACCESS signal.

Any kind of help would be greatly appreciated.

Misa
  • 889
  • 1
  • 10
  • 20
  • Frankly, I do not know where the immediate EXC_BAD_ACCESS comes from. BTW it would be helpful if you provided a bit more about the exception. What does the debugger say about it? Anyway, if you suspect the recursive approach to be responsible for the EXC_BAD_ACCESS, why don't you just call the problematicMethod using performSelector:withObject:afterDelay: ? withObject may be nil and afterDelay could even be 0. – Hermann Klecker Jul 11 '12 at 08:40
  • @HermannKlecker Actually there is nothing more that debugger can tell me. EXC_BAD_ACCESS will occur either when calling the [self problem...] method for the first time from ISNT_OK, or when it enters the method for the second time, because incorrect data was read (for example NSArray variable points to NSConcreteData data). – Misa Jul 11 '12 at 12:52
  • And the more I look at it, the more I'm certain that wrong memory location is used for accessing the data. I tried copying every single block with [block copy], and also leaking the memory (no autorelease), but without any luck. – Misa Jul 11 '12 at 12:55
  • Probably we need more code - what code is inside the block? You should also try debugging - put a breakpoint there and check on which exact call the app accesses bad data. – Sulthan Jul 11 '12 at 13:00

2 Answers2

2

How are you creating your block? Remember that you have to move it from stack to heap.

Example:

 void(^onCompleteBlock)(NSString*) = [[^(NSString* param) {
  //...block code
}] copy] autorelease];

[self problematicMethod:friendInfo onCompleteBlock];

Sulthan
  • 128,090
  • 22
  • 218
  • 270
  • I tried this, and although it looks to me like a perfect solution, it just doesn't work. – Misa Jul 11 '12 at 12:57
  • This is not correct. The calling code doesn't need to copy it because it is not storing it anywhere. – newacct Jul 11 '12 at 19:51
  • @newacct Blocks are created on stack. Copying a block moves it from stack to heap. I think this is pretty important in this context when the block is passed to a some response handler and used in some unknown time in future. – Sulthan Jul 11 '12 at 20:03
  • @Sulthan: yes, but who says it is used in some unknown time in the future? Memory management in Objective-C is completely local. One method does not need to care what other methods do. If a called method stores a block away somewhere, then it is that method, not the caller that created the block, that is responsible for copying it. From the caller's perspective, there is no difference between if it calls a method that uses the block immediately or stores it away for future use. – newacct Jul 12 '12 at 08:06
  • @newacct It doesn't really matter if you do as the first line of `problematicMethod` or even before calling it (but I agree it would be better inside the method). The code just has to be somewhere. The question doesn't show the code so I assumed it must be called before. – Sulthan Jul 12 '12 at 09:18
  • 1
    @Sulthan: but none of the code in `problematicMethod:onComplete:` that he shows actually stores the block in a variable for later use or anything. All it does is execute it or pass it down into deeper calls. If that's all it does it does not need to copy it. We don't know what `doSomethingWithFriend:onComplete:` does; if it needs to store its argument block for later use, *it* must copy it (not us); and copying it will automatically copy the `onComplete1` because it is a block that is captured by that block. So no, there should not be a copy anywhere in `problematicMethod:onComplete:`. – newacct Jul 12 '12 at 18:10
0

If response.status value is ISNT_OK you never finish calling recursively the function.

Ramy Al Zuhouri
  • 21,580
  • 26
  • 105
  • 187