iOSノート7:block


ブロック
1.ブロックとは何ですか.
ブロックは、{}内の一連の式のようなコードです.
呼び出しブロックのメソッドには、通常、「in-line」方式で含まれる.
次に、ブロックをパラメータとして呼び出す方法の例を示します.
[aDictionary enumerateKeysAndObjectsUsingblock:^(id key, id value, BOOL *stop) {
 NSLog(@"value for key %@ is %@", key, value);
 if ([@"ENOUGH" isEqualToString:key]) {
  *stop = YES;
 }
}];
これはJavaの匿名クラスと少し似ています.
blockは文字^で始まり、次に戻りタイプ(オプション)、パラメータ(オプション)、{,code}である.
blockでは、blockの前に定義ローカル変数(local variable)を用いることができる.
double stopValue = 53.5;
[aDictionary enumerateKeysAndObjectsUsingblock:^(id key, id value, BOOL *stop) {
 NSLog(@"value for key %@ is %@", key, value);
 if ([@"ENOUGH" isEqualToString:key] || ([value doubleValue] == stopValue)) {
  *stop = YES;
 }
}];
しかし、それらは読み取り専用です!
BOOL stopedEarly = NO;
double stopValue = 53.5;
[aDictionary enumerateKeysAndObjectsUsingblock:^(id key, id value, BOOL *stop) {
 NSLog(@"value for key %@ is %@", key, value);
 if ([@"ENOUGH" isEqualToString:key] || ([value doubleValue] == stopValue)) {
  *stop = YES;
  stoppedEarly = YES; // ILLEGAL!  
 }
}];
あなたが__でない限りblockはローカル変数を識別します:
__block BOOL stopedEarly = NO;
double stopValue = 53.5;
[aDictionary enumerateKeysAndObjectsUsingblock:^(id key, id value, BOOL *stop) {
 NSLog(@"value for key %@ is %@", key, value);
 if ([@"ENOUGH" isEqualToString:key] || ([value doubleValue] == stopValue)) {
  *stop = YES;
  stoppedEarly = YES; // this is legal now
 }
}];
if (stoppedEarly) NSLog(@"I stopped logging dictionary value early!");
ローカル変数はオブジェクトインスタンスであってもよいが、setter、getter方式でしかアクセスできない.
NSString *stopKey = [@"Enough" uppercaseString];
__block BOOL stopedEarly = NO;
double stopValue = 53.5;
[aDictionary enumerateKeysAndObjectsUsingblock:^(id key, id value, BOOL *stop) {
 NSLog(@"value for key %@ is %@", key, value);
 if ([stopKey isEqualToString:key] || ([value doubleValue] == stopValue)) {
  *stop = YES;
  stoppedEarly = YES; // this is legal now
 }
}];
if (stoppedEarly) NSLog(@"I stopped logging dictionary value early!");
blockではstopKeyの強い参照(strong)ポインタが自動的に保持され、blockが解放されるまで保持されます.
blockを正常に実行するには、明らかな要求です.
2.blockを保持できる変数タイプを作成します.
blockを持つ変数を宣言すると、blockは特殊な構文を持つオブジェクトに似ています.
一般的にtypedefを使用してタイプを宣言し、変数にblockを格納します.たとえば、次のようにします.
typedef double (^unary_operation_t)(double op);
以上のコードは、ブロックを格納できる「unary_operation_t」のタイプを宣言している.
次にunary_というタイプを定義できます.operation_t、squareという変数で、その変数に値を割り当てます.
unary_operation_t square;
square = ^(double operand) { // square block
 return operand * operand;
}
は次に、square変数を次のように使用します.
double squareOfFive = square(5.0); //  squareOfFile 25.0
typedefを強制的に使用する必要はありません.たとえば、次のコードもsquareを作成する合法的な方法です.
double (^square)(double op) = ^(double op) {return op * op;};
unary_を使用するには、次の方法があります.operation_t:
たとえばblock配列プロパティを使用できます.
@property (nonatomic, strong) NSMutableDictionary *unaryOperations;
は次に、このようにして1つの方法を実装する.
typedef double (^unary_operation_t)(double op);
-(void)addUnaryOperation:(NSString *)op whichExecutesBlock:(unary_operation_t)opBlock {
 self.unaryOperations[op] = opBlock;
}
blockはある程度対象とすることができることに注意する(e.g.adding it to a dictionary).
次に、辞書に追加されたアクションを使用します.
-(double)performOperation:(NSString *)operation onOperand:(double)operand
{
 unary_operation_t unaryOp = self.unaryOperations[operation];
 if(unaryOp) {
  double result = unaryOp(operand);
 }
 ...
}
typedefは必ずしも必要ではありません.blockがメソッドのパラメータであり、すぐに使用する必要がある場合、
typedefは常に必要ありません.以下は辞書の列挙方法の宣言です.
-(void)enumerateKeyAndObjectUsingBlock:(void (^)(id key, id obj, BOOL *stop))block;
以上の方法で定義するblockには名前がなく、名前がない以外はtypedefを使うのと同じ文法である.
パラメータ名「block」はパラメータのキーワードです.
typedefを使用する場合、上記のパラメータblockはこのように見えます.
typedef void (^enumeratingBlock)(id key, id obj, BOOL *stop);
ここでenumeratingBlockの部分はメソッドパラメータでは使用されません.
blockにはいくつかの略語の定義方法があります.blockにパラメータがない場合は、カッコを使用しないことができます.
以下のブロックをパラメータとする場合
[UIView animateWithDuration:5.0 animations:^() {
 view.opacity = 0.5;
}];
は、次のように書くことができます.
[UIView animateWithDuration:5.0 animations:^ {
 view.opacity = 0.5;
}];
はこれと同様に戻りタイプもあり、戻りタイプはしばしば推定され、このとき戻りタイプは省略することができる.
NSSet * mySet = ...;
NSSet * matches = [mySet objectsPassingTest:^(id obj, ...) {
 return [obj isKindOfClass:[UIView class]];
}];
return文の表面このblockはBOOLタイプを返しているので、上記の匿名blockを定義する際に戻りタイプは使用されません.
3.なぜblocksがオブジェクトのように表現できるのか(objects).
以上の学習面blocksによりobjects(in property,arrays,dictionarys,etc.)に記憶することができる.
実はblocksがobjectsのように表現する唯一の目的は、それらを格納することである(blockの唯一の「方法」はcopyである).
次のようになります.
@property (nonatomic, strong) NSMutableArray * myBlocks; // array for blocks
次のことができます
[self.myBlocks addObject:^{
 [self doSomething];
}];
とても簡潔で木があります!
また、この配列のblockを次のようにトリガー(呼び出し)することができます.
void (^doit)(void) = self.myBlocks[0];
doit();
ですが、これは危険です.
4.メモリ循環参照(交差参照).
blockで参照されているすべてのオブジェクトは、blockが解放されるまでスタックに保存されると述べた.
すなわち、blockは、参照されたすべてのオブジェクトのstrongポインタを有する.
上記の例ではselfはblockで参照されるオブジェクトです
[self .myBlocks addObject:^ {
 [self doSomething];
}];
ですのでこのblockはselfのstrongポインタを持っていますが、selfはblockのstrongポインタも持っているので注意してください.
このblockはmyBlocks配列にあるからです.
これは深刻な問題だ!!!
selfとblcokは、互いに相手の強いポインタを持っているため、スタックから解放されません.
これをメモリループリファレンスと呼びます.
メモリ循環参照のソリューション:
ローカル変数は常にstrongタイプであり、これは可能であることを思い出します.
プログラム呼び出しスタックがこの範囲を離れると、それらは消え、strongポインタも消えるからです.
ローカル変数がweakタイプであることを宣言できます.以下は方法です.
__weak MyClass *weakSelf = self; // even strong ,weakSelf weak 。

blockでweakSelfを参照すると、strongでselfを参照することはありません.
[self.myBlocks addObject:^ {
 [weakSelf doSomething];
}
メモリループリファレンスはなくなりました.selfのstrongポインタが外部にある限り、
blockのポインタは安全です.selfが解放されると、blockも解放されます.
5.iOSでは、いつblockを使いますか?
A.列挙、前述のNSDictionary.
B.ビューアニメーション;
C.並べ替え、blockを比較方法とする;
D.Notificationに通知し、あるイベントが発生した場合、blockを実行する.
E.エラーはError handlersを処理し、エラーが発生した場合blockを実行する.
F.Completion handlersの処理を終了し、あるタスクを完了した後、blockを実行する.
もう1つの超重要な使い方:マルチスレッド
Grand Central Dispatch(GCD)APIを使用する.