Objective-C回调处理程序

时间:2011-12-05 23:00:31

标签: objective-c ios methods callback objective-c-blocks

我有一个我必须工作的回调方法,但我想知道如何将值传递给它。

我拥有的是:

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

}


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

上面的代码有效,但我想将值传递给方法。这看起来怎么样?类似的东西:

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

1 个答案:

答案 0 :(得分:116)

我不完全确定你在那里尝试做什么 - 你的回调是一个障碍......这是故意的吗?我希望你的方法看起来像这样:

- (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...
                     }];

此方法调用会将用户登录到帐户中,然后在完成后立即显示余额。这显然是一个人为的例子,但希望你能得到这个想法。

如果这不是您想要的那种,那么只需使用如上所述的方法签名。


编辑(使用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由指针间接返回,例如在NSFileWrapper s -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).

然而,在登录示例中,我们可能希望登录尝试花费一些时间来完成(例如登录到在线帐户),因此使用完成处理程序通过错误回来。