Using blocks within blocks in Objective-C: EXC_BAD_ACCESS
Asked Answered
S

2

6

Using iOS 5's new TWRequest API, I've ran into a brick wall related with block usage.

What I need to do is upon receiving a successful response to a first request, immediately fire another one. On the completion block of the second request, I then notify success or failure of the multi-step operation.

Here's roughly what I'm doing:

- (void)doRequests
{
    TWRequest* firstRequest = [self createFirstRequest];
    [firstRequest performRequestWithHandler:^(NSData* responseData,
                                              NSHTTPURLResponse* response,
                                              NSError* error) {
        // Error handling hidden for the sake of brevity...
        TWRequest* secondRequest = [self createSecondRequest];
        [secondRequest performRequestWithHandler:^(NSData* a,
                                                   NSHTTPURLResponse* b,
                                                   NSError* c) {
            // Notify of success or failure - never reaches this far
        }];
    }];
}

I am not retaining either of the requests or keeping a reference to them anywhere; it's just fire-and-forget.

However, when I run the app, it crashes with EXC_BAD_ACCESS on:

[secondRequest performRequestWithHandler:...];

It executes the first request just fine, but when I try to launch a second one with a handler, it crashes. What's wrong with that code?


The methods to create the requests are as simple as:

- (TWRequest*)createFirstRequest
{
    NSString* target   = @"https://api.twitter.com/1/statuses/home_timeline.json";
    NSURL* url         = [NSURL URLWithString:target];
    TWRequest* request = [[TWRequest alloc]
                          initWithURL:url parameters:params
                          requestMethod:TWRequestMethodGET];
    // _twitterAccount is the backing ivar for property 'twitterAccount',
    // a strong & nonatomic property of type ACAccount*
    request.account    = _twitterAccount;

    return request;
}
Statolith answered 28/12, 2011 at 22:48 Comment(4)
please post the code of createFirstRequest and createSecondRequestShuddering
Can you post the code for -createSecondRequest? I'm guessing there's a problem with this code being run on a background thread since there's never a guarantee about which thread a completion handler will run on.Diarmit
Done; sorry 'bout that, didn't think it would be relevant.Statolith
@brunodecarvalho: if you want to know what's relevant, create a minimal test case whenever you need to post sample code.Soledadsolely
T
18

Make sure you're keeping a reference/retaining the ACAccountStore that owns the ACAccount you are using to sign the TWRequests.

If you don't, the ACAccount will become invalid and then you'll get EXC_BAD_ACCESS when trying to fire a TWRequest signed with it.

Titty answered 3/1, 2012 at 18:38 Comment(2)
Thanks! But it feels a little bit awkward. You need to save a reference to an object you're not going to set in the Twitter Request object. Very error prone.Stanton
I was not even trying to make any request to Twitter just grabbing the array of users on device and for the life of me could not figure out why I was getting a bad access on ACAccount .... @Property(nonatomic,retain)ACAccountStore *store; was the answer! THANK YOU FRED!Mortgagee
Z
1

I'm not familiar with TW*, so consider this a wild guess ... try sending a heap-allocated block:

[firstRequest performRequestWithHandler:[^ (NSData *responseData, ...) {
  ...
} copy]];

To clarify, I think the block you're sending is heap-allocated, so while TW* might be retaining it, it won't make any difference if it has already gone out of scope.

Zouave answered 31/12, 2011 at 5:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.