I’ve created a class which is a wrapper around NSURLConnection and users of the class pass blocks that get invoked at various stages of the connection. THe wrapper class is shown below, abbreviated for brevity:
typedef void (^ConnectionFinishedWithErrorBlock)(NSError* error);
@interface MYHTTPConnection : NSObject <NSURLConnectionDelegate>
@property (copy, nonatomic) ConnectionFinishedWithErrorBlock theErrorBlock;
@property (strong, nonatomic) NSURLConnection *connection;
- (void) establishConnectionForURL:(NSURL*) url
andConnectionFinishedWithErrorBlock: (ConnectionFinishedWithErrorBlock) errorBlock;
@end
So far I’ve been using it with the blocks declared inline i.e.
[self.httpConnection establishConnectionForURL: url
andConnectionFinishedWithErrorBlock:^(NSError* error)
{
...
}];
But now I have a situation where I have a very long error handling block, and to place all its code inline will get messy (as the API takes other blocks not shown here).
I know I could do something like this:
void (^httpConnectionFinishedWithError)(NSError*) =
^(NSError* error)
{
}
Then pass httpConnectionFinishedWithError to establishConnectionForURL. But httpConnectionFinishedWithError contains calls to self. The calls to self are fine when the block is declared inline, but not when done as above as obviously this generates compilation errors.
So I was wondering if/how the block could be made a block property of the calling class? (I’ve already used blocks as properties of classes before, as is done in the MYHTTPConnection class above which works fine, but in this situation I can’t get the syntax right to add the block that is passed to establishConnectionForURL as a property of the calling class).
Alternatively the httpConnectionFinishedWithError could just call another function, but in that situation I’d have to pass self as a parameter, how would I obtain self within the block in this case?
Are there other more elegant solutions?
Many thanks
EDIT:
Updated to show example of compilation error
void (^httpConnectionFinishedWithError)(NSError*) =
^(NSError* error)
{
self.boolProperty = NO; // here
}
If you’re using ARC, you can define a weak variable referring to self:
__weak ClassOfSelf *_self = self;and use that in the block. If you’re not using ARC I believe you want to use
__blockinstead of weak. In both cases the block will not retain self so you can avoid retain cycles.The best way I know of to store/use a block in class is to typedef it first:
typedef void (^HttpConnectionFinishedWithError)(NSError*);Then create an instance variable for the block storage:
HttpConnectionFinishedWithError httpCompletionBlock;In your init you create the block and assign it to the instance variable. Just remember to copy the block first so that it’s moved to the heap (blocks are created on the stack, which means they disappear after the scope they’re created in goes away):
(example using ARC)
Now
httpCompletionBlockcan be used like normal within the class. No retain cycles are created as long as you use_selfinstead ofselfin the block code and you don’t directly reference any instance variables within the block. Remember referencing_iVaris the same asself->_iVar. So the block will captureself. Instead use:_self->_iVaror create a property/get-method to access the instance variable.If you’re using ARC, but are targeting iOS 4, you can’t use
__weakbecause it’s not available and you can’t use__blockbecause it won’t prevent the block from retaining the variable under ARC. Instead, you can use__unsafe_unretained. Just remember that__unsafe_unretaineddoes not resolve tonilif the object the variable refers to is dealloc’d…so it’s unsafe. You need to be careful how you use it. For example, if you pass another object this block andselfgets dealloc’d. Any reference to _self will cause an error. A way to get around this is to create an inline “intermediate” block that will retain self only so long the block is needed. For example, instead of passing the block directly, do something like this:In the above case,
httpcompletionBlockis an instance variable, soselfget’s captured by the block in the local scope. You’re assured now thatselfwon’t go away until afterhttpCompletionBlockis executed. You’ve still avoided a retain cycle because only the inline block is retainingself, not the stored block.