Search code examples
ioscocoa-touchmemory-managementasihttprequestobjective-c-blocks

Blocks retaining the class they just got passed to?


We have a class that wraps NSURLConnection. It accepts a block that it calls back when it finishes loading. To give you an idea, see below. When you send a request, it saves the callback on the instance. Assume my class is named Request

// from Request.h
@property (nonatomic, copy) void(^callback)(Request*);
- (void) sendWithCallback:(void(^)(Request*))callback;

My code to use one looks something like this:

Request * request = [Request requestWithURL:url];
[request sendWithCallback:^(Request * request) {
    // do some stuff
}]

My question is: what does the block do to the retain count of request? Does it copy/retain it? Notice that I didn't put __block in front of the definition.

I just changed something major in Request (switched from a synchronous NSURLConnection to async ASIHTTPRequest), and it started deallocing almost immediately after sending (causing delegate methods to call a dealloced object). With the sync NSURLConnection, that never happened.

I guess it makes sense that it would get dealloced with async, but how would I retain request appropriately? If I retained it right after I created it, I'd have to release it in the callback, but the callback doesn't get called if the request is cancelled, and would create a memory leak.


Solution

  • what does the block do to the retain count of request? Does it copy/retain it?

    No, it doesn't.

    Request * request = [Request requestWithURL:url];
    [request sendWithCallback:^(Request * request) {
        // The request argument shadows the request local variable,
        // this block doesn't retain the request instance.
    }]
    

    If the block doesn't have the request argument,

    Request * request = [Request requestWithURL:url];
    [request sendWithCallback:^{
        // If you use the request local variable in this block,
        // this block automatically retains the request instance.
    }]
    

    In this case, it would cause retain cycles (the request retains the block, the block retains the request).

    Please take a look at my AsyncURLConnection class. NSURLConnection retains AsyncURLConnection instance, so you don't own AsyncURLConnection stuff by yourself.

    How to use

    [AsyncURLConnection request:url completeBlock:^(NSData *data) {
        // Do success stuff
    } errorBlock:^(NSError *error) {
        // Do error stuff
    }];