プロトコルを提供し、操作が完了したときにデリゲートメソッドを呼び出すクラスと通信する必要があるとしましょう。
@protocol SomeObjectDelegate
@required
- (void)stuffDone:(id)anObject;
- (void)stuffFailed;
@end
@interface SomeObject : NSObject
{
}
@end
さて、私はcould別のクラスにstuffDone:
デリゲートメソッドを実装させることができたが、私はむしろSomeObject
がインスタンス化されたり、呼び出されたりする場所の近くに書き込まれるブロックにプロセスをカプセル化します。これを行うにはどうすればよいですか?言い換えれば、 this ブロックに関する有名な記事(コールバックの置換セクション)を見ると、ある種のcompletionHandler:
を受け入れるSomeObjectのメソッドをどのように書くことができますか?
デリゲートオブジェクトを取得するように設計された既存のクラスと通信したいようです。次のような多くのアプローチがあります。
これが(3)を行う1つの方法です。まず、SomeObjectが次のようになっていると仮定しましょう。
_@protocol SomeObjectDelegate
@required
- (void)stuffDone:(id)anObject;
- (void)stuffFailed;
@end
@interface SomeObject : NSObject
{
}
+ (void) testCallback:(id<SomeObjectDelegate>)delegate;
@end
@implementation SomeObject
+ (void) testCallback:(id<SomeObjectDelegate>)delegate
{
[delegate stuffDone:[NSNumber numberWithInt:42]];
[delegate stuffFailed];
}
@end
_
したがって、テストする方法がいくつかあります。実際のSomeObjectがあります。
次に、プロトコルを実装し、提供されたブロックを呼び出すクラスを定義します。
_#import "SomeObject.h"
typedef void (^StuffDoneBlock)(id anObject);
typedef void (^StuffFailedBlock)();
@interface SomeObjectBlockDelegate : NSObject<SomeObjectDelegate>
{
StuffDoneBlock stuffDoneCallback;
StuffFailedBlock stuffFailedCallback;
}
- (id) initWithOnDone:(StuffDoneBlock)done andOnFail:(StuffFailedBlock)fail;
- (void)dealloc;
+ (SomeObjectBlockDelegate *) someObjectBlockDelegateWithOnDone:(StuffDoneBlock)done andOnFail:(StuffFailedBlock)fail;
// protocol
- (void)stuffDone:(id)anObject;
- (void)stuffFailed;
@end
_
このクラスは、渡したブロックを保存し、プロトコルコールバックに応答してそれらを呼び出します。実装は簡単です。
_@implementation SomeObjectBlockDelegate
- (id) initWithOnDone:(StuffDoneBlock)done andOnFail:(StuffFailedBlock)fail
{
if (self = [super init])
{
// copy blocks onto heap
stuffDoneCallback = Block_copy(done);
stuffFailedCallback = Block_copy(fail);
}
return self;
}
- (void)dealloc
{
Block_release(stuffDoneCallback);
Block_release(stuffFailedCallback);
[super dealloc];
}
+ (SomeObjectBlockDelegate *) someObjectBlockDelegateWithOnDone:(StuffDoneBlock)done andOnFail:(StuffFailedBlock)fail
{
return (SomeObjectBlockDelegate *)[[[SomeObjectBlockDelegate alloc] initWithOnDone:done andOnFail:fail] autorelease];
}
// protocol
- (void)stuffDone:(id)anObject
{
stuffDoneCallback(anObject);
}
- (void)stuffFailed
{
stuffFailedCallback();
}
@end
_
覚えておく必要があるのは、初期化時にブロックをBlock_copy()し、後でBlock_release()することだけです。これは、ブロックがスタックに割り当てられ、オブジェクトが作成中のスタックフレームよりも長持ちする可能性があるためです。 Block_copy()は、ヒープ内にコピーを作成します。
これで、すべてのデリゲートベースのメソッドがブロックを渡すことができます。
_[SomeObject testCallback:[SomeObjectBlockDelegate
someObjectBlockDelegateWithOnDone:^(id anObject) { NSLog(@"Done: %@", anObject); }
andOnFail:^{ NSLog(@"Failed"); }
]
];
_
この手法を使用して、任意のプロトコルのブロックをラップできます。
ARC補遺
コメントへの応答:このARCと互換性を持たせるには、Block_copy()
の呼び出しを削除して、直接割り当てを残します。
_stuffDoneCallback = done;
stuffFailedCallback = fail;
_
dealloc
メソッドを削除します。 Blockcopy
をcopy
に変更することもできます。つまり、_stuffDoneCallback = [done copy];
_です。これは、ARCのドキュメントを読むことで必要になると思われるものです。ただし、割り当てが強力な変数への割り当てであるため、ARCは割り当てられた値を保持します。スタックブロックを保持すると、それがヒープにコピーされます。したがって、生成されたARCコードは、copy
の有無にかかわらず同じ結果を生成します。
あなたはこのようなことをすることができます:
typedef void (^AZCallback)(NSError *);
AZCallback callback = ^(NSError *error) {
if (error == nil) {
NSLog(@"succeeded!");
} else {
NSLog(@"failed: %@", error);
}
};
SomeObject *o = [[SomeObject alloc] init];
[o setCallback:callback]; // you *MUST* -copy the block
[o doStuff];
...etc;
次に、SomeObject
内で、次のことができます。
if ([self hadError]) {
callback([self error]);
} else {
callback(nil);
}
以下のリンクは、デリゲートを使用したコールバックをブロックに簡単に置き換える方法を説明しています。
例には、UITableview、UIAlertview、およびModalViewControllerが含まれます。
お役に立てれば。