ブロックはいろいろなものに使われます。それらを使用する方法は、それらを非常に強力にすることができます。
たとえば、ブロックは、デリゲート メソッドが通常行うことを行う方法としてよく使用されます。つまり、ブロックはパラメーターとして渡されて脇に保管され、後で何らかの操作が終了したときに呼び出されます。そのようにして、それらは後で呼び出すことができるという事実で「非同期操作」として使用できます。
ブロックが非同期と見なされる使用例は、デリゲートよりも使いやすくするためにデリゲート メソッドで呼び出される場合です。
typedef void (^BlockAlertViewCompletion)(UIAlertView* alert, NSInteger clickedButton);
@interface BlockAlertView : UIAlertView <UIAlertViewDelegate>
-(void)showWithCompletion:(BlockAlertViewCompletion)completion;
@property(nonatomic, copy) BlockAlertViewCompletion completionBlock;
@end
@implementation BlockAlertView
@synthesize completionBlock = _completionBlock;
-(void)showWithCompletion:(BlockAlertViewCompletion)completion;
{
// Store the block for latter use
self.completionBlock = completion;
self.delegate = self; // Make ourselves as the delegate of UIAlertView
[self show];
}
// Then much later, the delegate method will be called ASYNCHRONOUSLY
-(void)alertView:(UIAlertView*)alert clickedButtonAtIndex:(NSInteger)buttonIndex
{
// Then call the block here to signal the called a button has been clicked
self.completionBlock(alert, buttonIndex);
}
-(void)dealloc { [_completionBlock release]; [super dealloc]; }
@end
// Usage:
BlockAlertView* alert = [[[BlockAlertView alloc] initWithTitle:@"Hello" message:@"Hello World!" delegate:nil cancelButtonTitle:@"Hi yourself" otherButtonTitles:@"I'm not very polite",nil];
// This will show the alert immediately (will call [alert show] eventually)
[alert showWithCompletion:^(UIAlertView* alert, NSInteger btnIndex)
{
// But this code in the block will only be called when the delegate method
// of UIAlertView will be triggered, so this code will be executed asynchronously / later, and not right away
NSLog(@"Button %d clicked", btnIndex);
}];
// So after the alert view has been shown on screen, the rest of the code below will continue executing
NSLog(@"The alert view has been shown on the screen, but the completionBlock passed a the parameter to showWithCompletion: has not been executed yet and will only execute when the delegate method is called");
[alert release];
ブロックは、厳密に言えば、他のコードとは独立して実際に実行されるわけではありません。基本的には、周囲の変数をキャプチャするためにコンテキストが追加された関数ポインターです。それらは、任意のスレッドで実行できます。以下の例のように、現在のスレッドでそれらを直接実行すると、それらは他のコードと並行しません。
// Declare some block
dispatch_block_t block = ^{ NSLog(@"Hello world"); };
// Execute it right away
block(); // this executes the same way that is you have wrote the NSLog here, so this is not concurrent with the rest of the code.
しかし、GCD を使用すると、独立したスレッドの GCD キューにブロックを追加し、そのスレッドにそのブロックを実行するように要求できます。その意味で、ブロックで宣言されたコードは、アプリケーションの残りの部分から独立して実行されます。
dispatch_block_t block = ^{ NSLog(@"Hello world"); };
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// Execute the block on a secondary queue, in parallel with the current code. This is concurrent execution, the block will execute independently to the rest of the code.
dispatch_async(queue, block);
また、「ブロックは他のコードとは独立して実行される」という考えを解釈することもできます。これは、ブロックがそのコンテキストと必要な変数をキャプチャし、ブロックが作成されたときにキャプチャされたこれらの変数の値を使用して後で実行できるという意味です。変数が変更されたか、および/または範囲外です:
-(void)createBlockAndCaptureVariables
{
int a = 5;
int b = 12;
// Create a block. It will capture the values of a and b at that time
dispatch_block_t block = ^{ NSLog(@"a*b = %d", a*b); };
// Even if you change the values of a and b later…
a = 7;
b = 25;
// when you execute the block here, it won't be affected by the fact that a and b have changed
[self testBlock:block]; // Will log a*b = 60
}
-(void)testBlock:(dispatch_block_t)aBlock
{
// And the block can be executed here and print the value of a*b, even if a and b
// Are out of scope and not accessible in this testBlock method.
aBlock();
}
これらの小さな例がお役に立てば幸いです。