0

最近、ASIHttpRequestライブラリからAFNetworkingに切り替えました。私はそのシンプルさが本当に好きですが、非同期コードを構造化する方法を理解するのにまだ苦労しています。

このサインアップシナリオを検討してください。

  • まず、入力したメールアドレスが利用可能かどうかを確認したいと思います。
  • 次に、入力したユーザー名が使用可能かどうかを確認します。
  • 上記の両方が有効で利用可能な場合は、実際のサインアップリクエストを送信します。

私のコードは次のようになります。

- (void)signUp{

    BOOL hasValidEmail = [self validateEmail:email];
    BOOL hasValidUsername = [self validateUsername:username];

    if(!hasValidEmail){
        NSLog(@"Invalid email");
        return;
    }

    if(!hasValidUsername){
        NSLog(@"Invalid username");
        return;
    }

    if (hasValidEmail && hasValidUsername) {
        NSLog(@"Go ahead and create account");
    }
}

ネットワークメソッドの非同期性を考慮すると、これをどのように構成するかはよくわかりません。多くの場合、前の2つの可用性チェックが応答を受け取る前に、最後の条件に到達します。

可用性チェックの方法は次のようになります。

- (BOOL)validateEmail:(NSString*)email{

    __block NSString* emailAlreadyExists = @"";

    NSString* url = [NSString stringWithFormat:@"user/emailexists/%@", email];

    [[APIClient sharedClient] getPath:url parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {

        emailAlreadyExists = [responseObject valueForKey:@"exists"];

    } failure:^(AFHTTPRequestOperation *operation, NSError* error) {
        NSLog(@"Email availability check failed: %@", error.localizedDescription);
    }];

    if([emailAlreadyExists isEqualToString:@"true"]){
        return NO;
    }

    return YES;   
}

改善が必要なのは私のブロックスキルだけかもしれませんが、このようなシナリオをどのように構成するのか、本当に聞きたいですか?

コードサンプルは「素晴らしい」ものですが、私はあなたが知っているパターンや優れたテクニックを本当に探しています。

ありがとう。

4

1 に答える 1

0

私は通常、これらをステップに分割し、前のステップが成功したときに次のステップを開始します。ブロックは、この目的のために渡すのに最適です。

これは明らかにコンパイルされませんが、うまくいけば、これを行う方法のいくつかのアイデアを与えることができます:

typedef enum
{
    SignupErrorNetworkError,
    SignupErrorEmailAlreadyTaken,
    SignupErrorUsernameAlreadyTaken,
} SignupError;

typedef enum
{
    // These steps must be performed in this order.
    SignupStepValidateEmail,
    SignupStepValidateUsername,
    SignupStepCreateAccount,
} SignupStep;

typedef void (^SignupSuccessBlock)();
typedef void (^SignupFailureBlock)(SignupError reason);

// Call this to sign up.
- (void)signupWithSuccess:(SignupSuccessBlock)success failure:(SignupFailureBlock)failure
{
    // Start the first step of the process.
    [self performSignupStep:SignupStepValidateEmail success:success failure:failure];
}

// Internal method. Don't call this from outside.
- (void)performSignupStep:(SignupStep)step success:(SignupSuccessBlock)success failure:(SignupFailureBlock)failure
{
    switch (step)
    {
        case SignupStepValidateEmail:
            [[APIClient sharedClient] getPath:url parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
                if ([responseObject valueForKey:@"exists"])
                {
                    if (failure) failure(SignupErrorEmailAlreadyTaken);
                    return;                }
                }
                // Start next async step in signup process.
                [self performSignupStep:SignupStepValidateUsername success:success failure:failure];
            } failure:^(AFHTTPRequestOperation *operation, NSError* error) {
                if (failure) failure(SignupErrorNetworkError);
            }];
            break;
        case SignupStepValidateUsername:
            // Similar to the step above. Starts the create account step on success.
            break;            
        case SignupStepCreateAccount:
            // Similar to the step above. Call the success block when done.
            break;            
    }
}

スイッチが長くなりすぎて醜い場合は、ステップを個別のメソッドにして、ステップ列挙型を削除することもできます。これは、などvalidateEmailWithSuccess:failureを呼び出して続行します。validateUsernameWithSuccess:failure

上記の例では、プロセスのステートマシンの性質を強調したかっただけです。

于 2012-04-24T20:00:23.693 に答える