Objective-Cでコールバックを実行する方法


119

Objective-Cでコールバック関数を実行する方法は?

完成した例をいくつか見たいのですが、それを理解する必要があります。

回答:


94

通常、目的Cのコールバックはデリゲートで行われます。以下は、カスタムデリゲート実装の例です。


ヘッダーファイル:

@interface MyClass : NSObject {
    id delegate;
}
- (void)setDelegate:(id)delegate;
- (void)doSomething;
@end

@interface NSObject(MyDelegateMethods)
- (void)myClassWillDoSomething:(MyClass *)myClass;
- (void)myClassDidDoSomething:(MyClass *)myClass;
@end

実装(.m)ファイル

@implementation MyClass
- (void)setDelegate:(id)aDelegate {
    delegate = aDelegate; /// Not retained
}

- (void)doSomething {
    [delegate myClassWillDoSomething:self];
    /* DO SOMETHING */
    [delegate myClassDidDoSomething:self];
}
@end

これは一般的なアプローチを示しています。NSObjectに、コールバックメソッドの名前を宣言するカテゴリを作成します。NSObjectは実際にはこれらのメソッドを実装していません。このタイプのカテゴリは非公式プロトコルと呼ばれ、多くのオブジェクトがこれらのメソッドを実装している可能性があるというだけです。これらは、セレクターの型シグニチャーを転送宣言する方法です。

次に、「MyClass」のデリゲートとなるオブジェクトがあり、MyClassは、デリゲートのデリゲートメソッドを適切に呼び出します。デリゲートコールバックがオプションの場合は、通常、 "if([delegate respondsToSelector:@selector(myClassWillDoSomething :)){"のようなものでディスパッチサイトでそれらを保護します。私の例では、デリゲートは両方のメソッドを実装する必要があります。

非公式のプロトコルの代わりに、@ protocolで定義された正式なプロトコルを使用することもできます。その場合は、デリゲートセッターのタイプとインスタンス変数をid <MyClassDelegate>、単に " id" ではなく " "に変更します。

また、デリゲートが保持されていないことにも気づくでしょう。これは通常、「MyClass」のインスタンスを「所有」するオブジェクトが通常はデリゲートでもあるために行われます。MyClassがデリゲートを保持している場合、保持サイクルがあります。MyClassインスタンスがあり、デリゲートであるクラスのdeallocメソッドでは、弱いバックポインターであるため、そのデリゲート参照をクリアすることをお勧めします。それ以外の場合、何かがMyClassインスタンスを存続させていると、宙ぶらりんのポインタが表示されます。


+1完全な答え。ケーキの上にアイシングは、デリゲートに関するより詳細なアップルのドキュメントへのリンクになります。:-)
クイン・テイラー

ジョン、助けてくれてありがとう。本当にありがとうございました。申し訳ありませんが、答えがはっきりしていません。Message .mは、doSomething関数呼び出し中に自身をデリゲートとして設定するクラスです。doSomethingはユーザーが呼び出すコールバック関数ですか?私はユーザーがdoSomethingを呼び出すという印象の下にあり、コールバック関数はmyClassWillDoSomethingg&myClassDidDoSomethingです。同様に、コールバック関数を呼び出すより高いクラスを作成する方法を教えていただけますか。私はCプログラマーなので、現時点ではObj-C環境に慣れていません。
ReachConnection

「メッセージ.m」は、.mファイルです。別のクラスがあるので、それを "Foo"と呼びましょう。Fooには「MyClass * myClass」変数があり、ある時点でFooは「[myClass setDelegate:self]」と発声します。その後の任意の時点で、fooを含む誰かがMyClassのインスタンスでdoSomethingMethodを呼び出した場合、fooはmyClassWillDoSomethingおよびmyClassDidDoSomethingメソッドを呼び出します。実際には、デリゲートを使用しない2つ目の別の例も投稿します。
ジョン・ヘス

.mは「メッセージ」の略ではないと思います。
チャック


139

完全を期すために、StackOverflow RSSは私のためにランダムに質問を復活させただけなので、他の(新しい)オプションはブロックを使用することです:

@interface MyClass: NSObject
{
    void (^_completionHandler)(int someParameter);
}

- (void) doSomethingWithCompletionHandler:(void(^)(int))handler;
@end


@implementation MyClass

- (void) doSomethingWithCompletionHandler:(void(^)(int))handler
{
    // NOTE: copying is very important if you'll call the callback asynchronously,
    // even with garbage collection!
    _completionHandler = [handler copy];

    // Do stuff, possibly asynchronously...
    int result = 5 + 3;

    // Call completion handler.
    _completionHandler(result);

    // Clean up.
    [_completionHandler release];
    _completionHandler = nil;
}

@end

...

MyClass *foo = [[MyClass alloc] init];
int x = 2;
[foo doSomethingWithCompletionHandler:^(int result){
    // Prints 10
    NSLog(@"%i", x + result);
}];

2
@Ahruman:「void(^ _completionHandler)(int someParameter);」の「^」文字とは 平均?その行が何をするか説明してもらえますか?
KonradHöffner、2012

2
コールバックハンドラーをコピーする必要がある理由を説明できますか?
Elliot Chance

52

以下は、デリゲートの概念を排除し、生のコールバックを行う例です。

@interface Foo : NSObject {
}
- (void)doSomethingAndNotifyObject:(id)object withSelector:(SEL)selector;
@end

@interface Bar : NSObject {
}
@end

@implementation Foo
- (void)doSomethingAndNotifyObject:(id)object withSelector:(SEL)selector {
    /* do lots of stuff */
    [object performSelector:selector withObject:self];
}
@end

@implementation Bar
- (void)aMethod {
    Foo *foo = [[[Foo alloc] init] autorelease];
    [foo doSomethingAndNotifyObject:self withSelector:@selector(fooIsDone:)];
}

- (void)fooIsDone:(id)sender {
    NSLog(@"Foo Is Done!");
}
@end

通常、メソッド-[Foo doSomethingAndNotifyObject:withSelector:]は非同期であり、コールバックがここよりも便利になります。


1
どうもありがとうジョン。コメント後の最初のコールバック実装を理解しました。また、2番目のコールバックの実装はより簡単です。どちらもとても良いです。
ReachConnection

1
このジョンを投稿してくれてありがとう、とても役に立ちました。[object performSelectorwithObject:self]を変更する必要がありました。to [object performSelector:selector withObject:self]; それを正しく機能させるために。
Banjer

16

この質問を最新に保つために、iOS 5.0のARCの導入は、ブロックをさらに簡潔に使用してこれを実現できることを意味します。

@interface Robot: NSObject
+ (void)sayHi:(void(^)(NSString *))callback;
@end

@implementation Robot
+ (void)sayHi:(void(^)(NSString *))callback {
    // Return a message to the callback
    callback(@"Hello to you too!");
}
@end

[Robot sayHi:^(NSString *reply){
  NSLog(@"%@", reply);
}];

Objective-Cのブロック構文を忘れた場合は、常にF **** ngブロック構文があります。


@interfaceには+ (void)sayHi:(void(^)(NSString *reply))callback;ないはずです+ (void)sayHi:(void(^)(NSString *))callback;
Tim007

前述のF **** ngブロック構文に従っていない:(- (void)someMethodThatTakesABlock:(returnType (^nullability)(parameterTypes))blockName;注でparameterTypesはないparameters
Ryan Brodie

4

コールバック:Objective Cには4種類のコールバックがあります

  1. セレクタのタイプ:NSTimer、UIPangestureがセレクタコールバックの例であることがわかります。コードの非常に限定された実行に使用されます。

  2. Delegate Type:一般的で、Appleフレームワークで最も使用されています。UITableViewDelegate、NSNURLConnectionDelegate。これらは通常、サーバーからの多数の画像の非同期ダウンロードなどを示すために使用されます。

  3. NSNotifications:NotificationCenterは、イベントが発生したときに多くの受信者に通知するために使用されるObjective Cの機能の1つです。
  4. ブロック:ブロックは、Objective Cプログラミングでより一般的に使用されます。これは優れた機能であり、コードのチャンクの実行に使用されます。チュートリアルを参照して理解することもできます:ブロックのチュートリアル

他にご不明な点がございましたらお聞かせください。よろしくお願いします。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.