web-dev-qa-db-ja.com

ブロックでコールバックロジックを単純化する方法は?

プロトコルを提供し、操作が完了したときにデリゲートメソッドを呼び出すクラスと通信する必要があるとしましょう。

@protocol SomeObjectDelegate

@required
- (void)stuffDone:(id)anObject;
- (void)stuffFailed;

@end

@interface SomeObject : NSObject
{
}
@end

さて、私はcould別のクラスにstuffDone:デリゲートメソッドを実装させることができたが、私はむしろSomeObjectがインスタンス化されたり、呼び出されたりする場所の近くに書き込まれるブロックにプロセスをカプセル化します。これを行うにはどうすればよいですか?言い換えれば、 this ブロックに関する有名な記事(コールバックの置換セクション)を見ると、ある種のcompletionHandler:を受け入れるSomeObjectのメソッドをどのように書くことができますか?

33
Alan Zeino

デリゲートオブジェクトを取得するように設計された既存のクラスと通信したいようです。次のような多くのアプローチがあります。

  1. カテゴリを使用して、適切なメソッドのブロックベースのバリアントを追加します。
  2. 派生クラスを使用して、ブロックベースのバリアントを追加します。そして
  3. プロトコルを実装し、ブロックを呼び出すクラスを作成します。

これが(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メソッドを削除します。 Blockcopycopyに変更することもできます。つまり、_stuffDoneCallback = [done copy];_です。これは、ARCのドキュメントを読むことで必要になると思われるものです。ただし、割り当てが強力な変数への割り当てであるため、ARCは割り当てられた値を保持します。スタックブロックを保持すると、それがヒープにコピーされます。したがって、生成されたARCコードは、copyの有無にかかわらず同じ結果を生成します。

42
CRD

あなたはこのようなことをすることができます:

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);
}
7
Dave DeLong

以下のリンクは、デリゲートを使用したコールバックをブロックに簡単に置き換える方法を説明しています。

例には、UITableview、UIAlertview、およびModalViewControllerが含まれます。

クリックしてください

お役に立てれば。

1
Durai Amuthan.H