Blocksを使うまでは、delegateを使って実装をしていたが、delegateだと呼び出し箇所と実行箇所が分離してしまい、可読性が悪くなることがある。
それにしても、Blocksは文法が覚えにくい自分の整理のために復習してみることにする。
基本の型
戻り値 (^Blocks名) (引数)
Blocks名の前に(^)キャレットがつくのが特徴のようです。
typedef
typedef 戻り値 (^Blocks名) (引数);
typedefを使うことでBlocksをまるっと変数の型の用に宣言することができる。
メソッド引数
正直これが覚えにくい
Blocksが引数として利用されると形が変形します。
- (void)loadImageCompletion:(BOOL(^)(BOOL canceled))completion;
戻り値 (^Blocks名) (引数) ※基本型
↓
↓ 名前を後ろへ切り出す ←メソッド引数名は最後にくるから
↓
戻り値 (^) (引数)Blocks名
↓
↓ 引数の型となる範囲を( )で囲う ←メソッド標準の書き方
↓
(戻り値 (^) (引数)) Blocks名
プロパティ宣言
@property (copy) 戻り値 (^Blocks名) (引数)
Blocksの実装部分
^戻り値 (引数) {...}
実装部分の場合は、Blocks名が不要なのは当然だが^(キャレット)が戻り値の前に移動する。
これも基本の型と異なる。但し、Xcodeの補完機能を使えば実装部分は勝手に補完されるので
正確に覚えておかなくても実装は可能だ。
サンプル
typedef
// typedef宣言
typedef BOOL (^compareNumber) (NSString *, NSNumber *);
// 実装
compareNumber equals = ^(NSString *string, NSNumber *number){
if ([string isEqualToString:[number stringValue]]){
return YES;
}
return NO;
};
// 利用
NSLog(@"result:%zd",equals(@"111",@111));
NSLog(@"result:%zd",equals(@"112",@111));
引数
javaScriptのmapのようなものを作ってみたいと思います。
blocksメソッド引数利用の実装例
ここではNSArrayのカテゴリとして実装
メソッド引数宣言部分
// NSArray+Hoge.h
- (NSArray *)mapWithBlocks:(id(^)(id))blocks;
// NSArrary+Hoge.m
- (NSArray *)mapWithBlocks:(id(^)(id))blocks
{
NSMutableArray *results = [NSMutableArray array];
[self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
id value = blocks(obj);
[results addObject:value];
}];
return results;
}
実際に使ってみます。
HogeEntityという型のEntityの配列からtitleプロパティを抽出するサンプル
ダミーデータ準備
// HogeEntity.h
@interface HogeEntity : NSObject
@property (nonatomic, copy) NSString *title;
@property (nonatomic, copy) NSString *context;
@end
// ダミーデータ作成
self.array1 = [NSMutableArray array];
for (NSInteger i=0; i < 10; i++) {
HogeEntity *entity = [HogeEntity new];
entity.title = [NSString stringWithFormat:@"タイトル%zd",i];
entity.context = [NSString stringWithFormat:@"本文%zd",i];
[self.array1 addObject:entity];
}
メソッド引数として実行している部分
// Blocks引数を使ってtitleを抽出して配列として出力
NSArray *results = [self.array1 mapWithBlocks:^id(HogeEntity *entity) {
return entity.title;
}];
ログ出力
// resultsの結果をログに出力
[results enumerateObjectsUsingBlock:^(NSString *title, NSUInteger idx, BOOL *stop) {
NSLog(@"[%zd]%@",idx,title);
}];
// コンソール結果
2014-12-31 18:53:52.359 Test3[12509:4640980] [0]本文0
2014-12-31 18:53:52.359 Test3[12509:4640980] [1]本文1
2014-12-31 18:53:52.359 Test3[12509:4640980] [2]本文2
2014-12-31 18:53:52.359 Test3[12509:4640980] [3]本文3
2014-12-31 18:53:52.359 Test3[12509:4640980] [4]本文4
2014-12-31 18:53:52.359 Test3[12509:4640980] [5]本文5
2014-12-31 18:53:52.359 Test3[12509:4640980] [6]本文6
2014-12-31 18:53:52.360 Test3[12509:4640980] [7]本文7
2014-12-31 18:53:52.360 Test3[12509:4640980] [8]本文8
2014-12-31 18:53:52.360 Test3[12509:4640980] [9]本文9
おまけ
javaScript風filter
- (NSArray *)filterWithBlock:(BOOL (^)(id))blocks
{
NSMutableArray *results = [NSMutableArray array];
[self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
if (blocks(obj)) {
[results addObject:obj];
}
}];
return results;
}
// タイトル4を除外
results = [self.array1 filterWithBlock:^BOOL(HogeEntity *entity) {
if (![entity.title isEqualToString:@"タイトル4"]) {
return YES;
}
return NO;
}];
[results enumerateObjectsUsingBlock:^(HogeEntity *entity, NSUInteger idx, BOOL *stop) {
NSLog(@"[%zd]%@",idx,entity.title);
}];
// ログ
2014-12-31 19:32:34.608 Test3[13019:4659567] [0]タイトル0
2014-12-31 19:32:34.608 Test3[13019:4659567] [1]タイトル1
2014-12-31 19:32:34.608 Test3[13019:4659567] [2]タイトル2
2014-12-31 19:32:34.608 Test3[13019:4659567] [3]タイトル3
2014-12-31 19:32:34.609 Test3[13019:4659567] [4]タイトル5
2014-12-31 19:32:34.609 Test3[13019:4659567] [5]タイトル6
2014-12-31 19:32:34.609 Test3[13019:4659567] [6]タイトル7
2014-12-31 19:32:34.609 Test3[13019:4659567] [7]タイトル8
2014-12-31 19:32:34.609 Test3[13019:4659567] [8]タイトル9
まとめ
Blocksは、基本の型、メソッド引数として利用した場合の型、実装の型の3つが存在するようだ。
ひとまず、実装の型はXcodeの補完に任せるとして、基本の型、メソッド引数として利用した場合の型の
2つを覚えれば実装可能だと思います。
それでは、皆様良いお年をお迎えください〜