2011-12-05 7 views
31

コールバックメソッドがありますが、値を渡す方法を知りたいです。作品上記のコードObjective-Cコールバックハンドラ

@interface DataAccessor : NSObject 
{ 
    void (^_completionHandler)(Account *someParameter); 

} 


- (void) signInAccount:(void(^)(Account *))handler; 

が、私は、メソッドに値を渡したい:私が持っているもの

はこれです。どのように見えるでしょうか?次のようなもの:

- (void) signInAccount:(void(^)(Account *))handler user:(NSString *) userName pass:(NSString *) passWord; 

答えて

112

私はあなたが何をしようとしているかは完全にはわかりません。あなたのコールバックはブロックです...その意図的ですか?私はあなたの方法は、このような何かを見て期待:あなたのコールバックの意図は完了時に(あなたがメソッドを呼び出したときに指定された)いくつかの追加のコードを実行することです

- (void)signInAccountWithUserName:(NSString *)userName password:(NSString *)password; 

場合、ブロックは有用であろう。

- (void)signInAccountWithUserName:(NSString *)userName 
         password:(NSString *)password 
         completion:(void (^)(void))completionBlock 
{ 
    // ... 
    // Log into the account with `userName` and `password`... 
    // 

    if (successful) { 
     completionBlock(); 
    } 
} 

をそしてそうのようなメソッドを呼び出します:たとえば、あなたの方法は次のようになり

[self signInAccountWithUserName:@"Bob" 
         password:@"BobsPassword" 
        completion:^{ 
         [self displayBalance]; // For example... 
        }]; 

このメソッドの呼び出しは、すぐにそれが完了すると、アカウントにユーザーをログインします、バランスを示す。これは明らかに工夫された例ですが、うまくいけばアイデアを得ることができます。

これが意図したものでない場合は、上記のようなメソッドシグネチャを使用するだけです。


EDIT(successful変数を使用して、より良い例):

より良いデザインはログインが行ってどれだけ説明し、完了ブロックに戻っブールを渡すために、次のようになります。

- (void)signInAccountWithUserName:(NSString *)userName 
         password:(NSString *)password 
         completion:(void (^)(BOOL success))completionBlock 
{ 
    // Log into the account with `userName` and `password`... 
    // BOOL loginSuccessful = [LoginManager contrivedLoginMethod]; 

    // Notice that we are passing a BOOL back to the completion block. 
    if (completionBlock != nil) completionBlock(loginSuccessful); 
} 

今回は、completionBlockのパラメータがnilでないことを確認しています。これは、呼び出し前には重要です使用するなし完了ブロック。あなたはそうのように、このメソッドを使用する場合があります:

[self signInAccountWithUserName:@"Bob" 
         password:@"BobsPassword" 
        completion:^(BOOL success) { 
         if (success) { 
          [self displayBalance]; 
         } else { 
          // Could not log in. Display alert to user. 
         } 
        }]; 

まだよく(あなたが例の帯状の言い訳ことができれば!)、それはユーザーNSErrorオブジェクトを返し、失敗の理由を知るために有用であろうとします。

- (void)signInAccountWithUserName:(NSString *)userName 
         password:(NSString *)password 
         completion:(void (^)(NSError *error))completionBlock 
{ 
    // Attempt to log into the account with `userName` and `password`... 

    if (loginSuccessful) { 
     // Login went ok. Call the completion block with no error object. 
     if (completionBlock != nil) completionBlock(nil); 
    } else { 
     // Create an error object. (N.B. `userInfo` can contain lots of handy 
     // things! Check out the NSError Class Reference for details...) 
     NSInteger errorCode; 
     if (passwordIncorrect) { 
      errorCode = kPasswordIncorrectErrorCode; 
     } else { 
      errorCode = kUnknownErrorCode; 
     } 
     NSError *error = [NSError errorWithDomain:MyLoginErrorDomain code:errorCode userInfo:nil]; 
     if (completionBlock != nil) completionBlock(error); 
    } 
} 

呼び出し側はその後、(何が悪かったのか、ユーザーに記述するために、最も可能性が高い)続行する方法を決定するために、完了ブロックでNSErrorを利用することができます。この種のパターンは(完全に有効ですが)やや一般的です。ほとんどNSError sはNSFileWrapper-initWithURL:options:error:方法、例えば、ポインタの間接によって返されます。ログインの例で

NSError *error; 
NSFileWrapper *fw = [[NSFileWrapper alloc] initWithURL:url options:0 error:&error]; 
// After the above method has been called, `error` is either `nil` (if all went well), 
// or non-`nil` (if something went wrong). 

、しかし、我々はおそらく、例えば(完了するまでにある程度の時間を取るためにログイン試行を期待していますオンラインアカウントにログインする)ので、エラーを返す完了ハンドラを使用することは完全に合理的です。

+0

ありがとう、これは私を助けました。はい、それは意図的です。 – Jesse

+2

私はおそらく2つのブロックでそれを設計するだろう:成功と失敗 – vikingosegundo

+1

@vikingosegundo:はい、良い提案。コンテキストによっては、その成功を 'BOOL'として返す方が簡単かもしれません。あるいは、ポインタインダイレクションによって' NSError'オブジェクトを渡して、サインインがどれほどうまくいったのかを判断することもできます。 – Stuart

関連する問題