Objective-C 回调处理程序

36

我有一个回调函数,它已经可以正常工作,但我想知道如何向它传递值。

我的代码如下:

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

}


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

上面的代码可以工作,但我想把值传递给这个方法。应该如何实现?大概是这样:

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

?

1个回答

122

我不完全确定你在尝试做什么 - 你的回调函数是一个块...这是有意为之吗?我期望你的方法看起来像这样:

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

如果你的回调函数的意图是在完成时执行一些额外的代码(在调用该方法时指定),那么使用 block 将会很有用。例如,你的方法将会像这样:

- (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-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).

然而,在登录示例中,我们可能希望登录尝试需要一些时间才能完成(例如登录到在线账户),因此使用传递错误的完成处理程序是完全合理的。


1
@vikingosegundo:是的,好建议。虽然,根据上下文,将成功返回为BOOL可能更容易,或者可能通过指针间接传递一个NSError对象来确定登录的情况如何。 - Stuart
有人能解释一下 if (successful) 是什么意思吗?我该如何定义 successful?我猜它是一个 BOOL 类型的变量? - sixstatesaway
2
在上面的例子中,successful只是一个假定的本地变量,用于描述登录是否成功。更好的解决方案可能是在完成块中返回该布尔值。我已经对答案进行了编辑,以说明这一点。 - Stuart
@Stuart 你会像完成处理程序一样做吗?https://stackoverflow.com/questions/49857236/trigger-local-notifications-automatically-daily-on-dynamic-time-given-in-arrays - iOS Developer
Swift func myFunction(str: String, completionHandler: @escaping (String) -> ()){ completionHandler("") } myFunction(str: "someThing", completionHandler: {(str) in}) - ZAFAR007

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接