Here’s some code that takes a block called success.
AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) { // city NSString *city = [JSON valueForKeyPath:@"location.city"]; NSString *currentTemp = [JSON valueForKeyPath:@"current_observation.temp_c"]; // create a weather object Weather *weather = [[Weather alloc] init]; weather.city = city; weather.currentTemperature = [currentTemp intValue]; // notify! NSDictionary *dictionaryWithWeather = [NSDictionary dictionaryWithObject:weather forKey:WEATHER_KEY]; [[NSNotificationCenter defaultCenter] postNotificationName:WEATHER_KEY object:self userInfo:dictionaryWithWeather]; } ...
Cool. But what if I want to pull out that ‘success’ code out into a variable and pass it to the calling method. Here’s what it looks like.
First define a typedef for the block:
typedef void (^success)(NSURLRequest *request, NSHTTPURLResponse *response, id JSON);
You can figure out the typedef method signature by looking at the underlying code, and then just sticking the word typdef in front of it. Note ‘success’ is the name of typdef here.
Then define a variable for the block:
success requestSuccess;
Nothing magical. But I want you to see what assigning a variable to a block looks like. It looks like any other variable assignment.
Then instantiate your method and define the contents of the block.
requestSuccess = ^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) { // city NSString *city = [JSON valueForKeyPath:@"location.city"]; NSString *currentTemp = [JSON valueForKeyPath:@"current_observation.temp_c"]; // create a weather object Weather *weather = [[Weather alloc] init]; weather.city = city; weather.currentTemperature = [currentTemp intValue]; // notify! NSDictionary *dictionaryWithWeather = [NSDictionary dictionaryWithObject:weather forKey:WEATHER_KEY]; [[NSNotificationCenter defaultCenter] postNotificationName:WEATHER_KEY object:self userInfo:dictionaryWithWeather]; };
Then replace the code you pulled out with the call to the block variable:
AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) { requestSuccess(request, response, JSON); }
Voila! Cleaner more readable code.
sgleadow
May 15, 2012 @ 11:15:50
I often use the same block typedef syntax, most commonly for UIView animation code. Two reasons I second guess myself on this now:
– if the block is so big I need to put it in a variable, you lose the in line readability that blocks give, so I either extract the logic so the block is 1-3 lines and can be put inline, or I move back to delegation pattern
– if I get tempted to create a @property for the block to save it later. This is the number one cause of memory leaks in my code, so when I get to the point of storing blocks in properties I usually move back to delegation as well
JR
May 15, 2012 @ 16:39:17
Hmm good points Stuart. I hear you on the readability. Having the code inlined is nice because you can see everything there.
The memory leak is something I hadn’t thought of, but I could see giving people pause. If I was getting memory leaks like you were, I would probably go back to inlining it to.
Great points. Thanks so much for the f/b.