[UIView animateWithDuration:5.0
animations:^{
// some code
} completion:^(BOOL finished) {
// some code
}];
[array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
// some code
}];
Blocks enable you to write more compact and flexible codes that can be easily reused in your programs. It does take awhile to get use to the most weird looking syntax though.
Then you start to think, wouldn't it be nice if I could use a block based completion handler in some of my methods. But how? It looks so confusing and you just don't know where to begin. The block syntax makes it appears harder than it really is. The basic pattern is simple.
The Pattern
typedef void (^NameOfYourCompletionHandler)(NSString *name, BOOL finished);
This block calls 'NameOfYourCompletionHandler'. It has two arguments - an NSString and a BOOL. Obviously, you can have more or less arguments to suit your situation.
2. Add a property
@property (nonatomic, copy) NameOfYourCompletionHandler nameOfYourCompletionHandler;
This is just like how you define any other property. i.e. preceding the name of the property with the type (i.e. NameOfYourCompletionHandler here). The name of the property can be whatever you like. If the code uses only one property of this block type, it is generally preferable to keep the property name the same as the name of the type (and lower casing the first character) to make it easier to follow.
3. Create an instance method (not a class method) that takes the block as one of its argument
- (void)doSomeTask:(NSString *)taskName withCompletion:(NameOfYourCompletionHandler)completionBlock
{
self.nameOfYourCompletionHandler = completionBlock;
// code to do something
}
The line self.nameOfYourCompletionHandler = completionBlock is important as this copies the completionBlock code to the instance variable so that it can be invoked later.
As noted above, this cannot be a class method as a class method would have any instance variables, such as the nameOfYourCompletionHandler here. If you must use a class method, you need to use static storage instead.
4. Call the completion block when you want to invoke it.
self.nameOfYourCompletionHandler(@"some name", YES);
This simply calls the completion block code and passes in the arguments.
This is all you need. And you can use the block based instance method like this, assuming you are invoking it within the same class:
[self doSomeTask:@"some task name" withCompletion:^(NSString *name, BOOL finished) {
// code you want to perform
// i.e. code you want to execute when the completion block is invoked under (4) above.
}];
Looking around online you will come across slight variation to the above, but the underlying pattern is the same. Some don't use typedef, and some don't use property (using static storage instead).
Without Typedef
Without using typedef, (2) would look like:
@property (nonatomic, copy) void (^nameOfYourCompletionHandler)(NSString *, BOOL)
And (3) would look like:
- (void)doSomeTask:(NSString *)taskName withCompletion:(void(^)(NSString *name, BOOL finished))completionBlock
{
self.nameOfYourCompletionHandler = completionBlock;
// code to do something
}
The rest would be the same.
Typedef makes the code looks cleaner (i.e. less weird syntax). However, it is just a personal preference/ coding style thing.
Using Static Storage
If you want to use block with class method you can do so with static storage.
Instead of (2), you would declare the static storage as follows in your implementation file:
static NameOfYourCompletionHandler _nameOfYourCompletionHandler;
And (3), would look like:
+ (SomeClass*)doSomeTask:(NSString *)taskName withCompletion:(NameOfYourCompletionHandler)completionBlock
{
_nameOfYourCompletionHandler = [completionBlock copy];
// code to do something
}
And (4) becomes:
_nameOfYourCompletionHandler(@"some name", YES);
Other Useful Reference About Using Block
- Working with Blocks
- Using Blocks in iOS 4: The Basics