web-dev-qa-db-fra.com

Gestionnaire de rappel Objective-C

J'ai une méthode de rappel que j'ai mise au travail, mais je veux savoir comment lui transmettre des valeurs.

Ce que j'ai c'est ceci:

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

}


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

Le code ci-dessus fonctionne, mais je veux transmettre des valeurs à la méthode. À quoi cela ressemblerait-il? Quelque chose comme:

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

?

34
Jesse

Je ne suis pas tout à fait sûr de ce que vous essayez de faire là-bas - votre rappel est un bloc ... est-ce intentionnel? Je m'attendrais à ce que votre méthode ressemble à ceci:

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

Si l'intention de votre rappel est d'exécuter du code supplémentaire (spécifié lorsque vous appelez la méthode) à la fin, un bloc serait utile. Par exemple, votre méthode ressemblerait à ceci:

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

    if (successful) {
        completionBlock();
    }
}

Et puis appelez la méthode comme ceci:

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

Cet appel de méthode connecterait l'utilisateur au compte, puis dès que cela serait terminé, afficherait le solde. Il s'agit clairement d'un exemple artificiel, mais j'espère que vous aurez l'idée.

Si ce n'est pas le genre de chose que vous vouliez, utilisez simplement une signature de méthode comme celle ci-dessus.


EDIT (Un meilleur exemple en utilisant la variable successful):

Une meilleure conception serait de renvoyer un booléen dans le bloc d'achèvement qui décrit la qualité de la connexion:

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

Vous verrez également que cette fois-ci, nous vérifions que le paramètre completionBlock n'est pas nil avant de l'appeler - ceci est important si vous souhaitez autoriser l'utilisation de la méthode sans un bloc de complétion. Vous pouvez utiliser cette méthode comme ceci:

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

Mieux encore (si vous pouvez excuser les bandes d'exemples!), S'il serait utile pour l'utilisateur de connaître la raison de l'échec, renvoyez un objet 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);
    }
}

L'appelant peut alors utiliser le NSError dans le bloc d'achèvement pour décider comment procéder (très probablement, pour décrire à l'utilisateur ce qui n'a pas fonctionné). Ce type de modèle est légèrement moins courant (bien que parfaitement valide); la plupart du temps, NSErrors sont retournés par l'indirection de pointeur, par exemple dans le NSFileWrappers -initWithURL:options:error: méthode:

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

Dans l'exemple de connexion, cependant, nous nous attendons probablement à ce que la tentative de connexion prenne un certain temps (par exemple, la connexion à un compte en ligne), il est donc parfaitement raisonnable d'utiliser un gestionnaire d'achèvement qui transmet une erreur.

116
Stuart