webentwicklung-frage-antwort-db.com.de

Objective-C-Callback-Handler

Ich habe eine Rückrufmethode, mit der ich arbeiten konnte, aber ich möchte wissen, wie ich Werte an sie übergeben kann.

Was ich habe, ist das:

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

}


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

Der obige Code funktioniert, aber ich möchte Werte an die Methode übergeben. Wie würde das aussehen? Etwas wie:

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

?

34
Jesse

Ich bin nicht ganz sicher, was Sie dort versuchen - Ihr Rückruf ist ein Block ... ist das beabsichtigt? Ich würde erwarten, dass Ihre Methode ungefähr so ​​aussieht:

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

Wenn der Zweck Ihres Rückrufs darin besteht, nach Abschluss einen zusätzlichen Code (der beim Aufrufen der Methode angegeben wird) auszuführen, ist ein Block hilfreich. Ihre Methode würde beispielsweise so aussehen:

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

    if (successful) {
        completionBlock();
    }
}

Und dann rufen Sie die Methode wie folgt auf:

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

Dieser Methodenaufruf würde den Benutzer in das Konto einloggen und dann, sobald dies abgeschlossen ist, den Kontostand anzeigen. Dies ist eindeutig ein erfundenes Beispiel, aber Sie werden hoffentlich auf die Idee kommen.

Wenn dies nicht der Fall ist, den Sie beabsichtigt haben, verwenden Sie einfach eine Methodensignatur wie die oben beschriebene.


EDIT (Ein besseres Beispiel mit der Variablen successful):

Ein besseres Design wäre, einen Booleschen Wert in den Vervollständigungsblock zurückzugeben, der beschreibt, wie gut die Anmeldung gelaufen ist:

- (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);
}

Sie werden auch feststellen, dass wir diesmal überprüfen, ob der Parameter completionBlock nicht nil ist, bevor Sie ihn aufrufen - dies ist wichtig, wenn Sie zulassen möchten, dass die Methode verwendet wird ohne ein Vervollständigungsblock. Sie können diese Methode folgendermaßen verwenden:

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

Besser noch (wenn Sie die Schwaden der Beispiele entschuldigen können!), Wenn es für den Benutzer nützlich wäre, den Grund für den Fehler zu kennen, geben Sie ein NSError -Objekt zurück:

- (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);
    }
}

Der Aufrufer kann dann das NSError im Vervollständigungsblock verwenden, um zu entscheiden, wie er vorgehen soll (um dem Benutzer höchstwahrscheinlich zu beschreiben, was schief gelaufen ist). Diese Art von Muster ist etwas weniger verbreitet (obwohl es vollkommen gültig ist); Meist werden NSErrors durch Zeiger-Indirektion zurückgegeben, zum Beispiel in NSFileWrappers -initWithURL:options:error: Methode:

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

Im Anmeldebeispiel ist jedoch zu erwarten, dass der Anmeldeversuch einige Zeit in Anspruch nimmt (z. B. das Anmelden bei einem Online-Konto). Daher ist es durchaus sinnvoll, einen Completion-Handler zu verwenden, der einen Fehler zurückgibt.

116
Stuart