Objective-C完成通话永远不会完成

时间:2018-06-27 03:38:33

标签: ios objective-c completionhandler

我正在尝试在阻塞调用中发送实例并等待其完成,因此稍后我可以在程序中使用该值,但完成功能永远不会完成。我对Objective-C极为陌生,我只使用Objective-c作为包装器,所以我不知道为什么我的完成调用永远不会结束。

[sharedClient createTokenWithCard:cardParams completion:^(STPToken *token,NSError *error) {
    if (token == nil || error != nil) {
        tokenChar = [error.localizedDescription UTF8String];
    }
    else{
        tokenChar = [token.tokenId UTF8String];
    }
}];
while(tokenChar == nil){
}
return tokenChar;

所以现在我将方法更改为此

void StripeWrapper::retrieveToken:(id)(char* myKey, char* cardNumber, int expMonth, int expYear, char* cvc) completion:(void (^)(NSString *))completion {
    NSString* NScardNumber = [NSString stringWithUTF8String:cardNumber];
    NSString* NScvc = [NSString stringWithUTF8String:cvc];

    STPCardParams *cardParams = [[STPCardParams alloc] init];
    cardParams.number = NScardNumber;
    cardParams.expMonth = expMonth;
    cardParams.expYear = expYear;
    cardParams.cvc = NScvc;

    NSString *myPublishableKey = [NSString stringWithUTF8String:myKey];
    STPAPIClient *sharedClient = [[STPAPIClient alloc] initWithPublishableKey:myPublishableKey];

    [sharedClient createTokenWithCard:cardParams completion:^(STPToken *token,NSError *error) {
        NSString *tokenChar;
        if (token == nil || error != nil) {
            tokenChar = [error.localizedDescription UTF8String];
        } else {
            tokenChar = [token.tokenId UTF8String];
        }
        if (completion) completion(tokenChar);
    }];
}

2 个答案:

答案 0 :(得分:0)

自从我处理了Objective-c以来已经有一段时间了,所以请谅解我在语法上的错误。

// This just uses completion block and will call the completion once the createTokenWithCard function finishes its execution
- (void) someFunctionName: (void (^)(NSString * theThingIWant, NSError *error)) completion { 

         [sharedClient createTokenWithCard:cardParams completion:^(STPToken *token,NSError *error) {
                      if (token == nil || error != nil) {
                           tokenChar = [error.localizedDescription UTF8String];
                       } else{
                              tokenChar = [token.tokenId UTF8String];
                      }
                     completion(tokenChar, error)
          }];   
}
// And in your caller its like
   [self someFunctionName:^(NSString *some, NSError *error) {
         // do the thing you want here
   }];



// Second approach will be dispatches. This will wait for createTokenWithCard before it returns
 - (void) someFunctionName: (id) cardParams { 
         __block NSString * theThingYouNeed;
         dispatch_group_t someGroupName = dispatch_group_create();

         [sharedClient createTokenWithCard:cardParams completion:^(STPToken *token,NSError *error) {
                      if (token == nil || error != nil) {
                           theThingYouNeed = [error.localizedDescription UTF8String];
                       } else{
                              theThingYouNeed = [token.tokenId UTF8String];
                      }
                     dispatch_group_leave(someGroupName);
          }];

        // this will wait forever so try to have a timeout I guess.
        dispatch_group_wait(someGroupName,DISPATCH_TIME_FOREVER);
        return theThingYouNeed
}

答案 1 :(得分:0)

异步方法有一种激增的方式,通常也迫使其调用者异步。换句话说,如果methodA的结果取决于methodB,并且methodB是异步的,那么methodA也必须是异步的。

因此包含OP代码的方法可能应该这样声明:

- (void)getMyTokenChar:(id)someParams completion:(void (^)(NSString *))completion {
    // form cardParams with someParams (or maybe they are the same
    [sharedClient createTokenWithCard:cardParams completion:^(STPToken *token,NSError *error) {
        NSString *tokenChar;
        if (token == nil || error != nil) {
            tokenChar = [error.localizedDescription UTF8String];
        } else {
            tokenChar = [token.tokenId UTF8String];
        }
        if (completion) completion(tokenChar);
    }];
}

呼叫者会这样做...

[theTokenCharObject getMyTokenChar:@"someParams" completion:^(NSString *tokenChar) {
    // tokenChar will be a string or an error description here
}];

这是个坏消息,包含调用代码的方法可能也需要异步。它永远不会结束吗?

是的,它通常在用户界面上结束...

// do something to the UI to say the app is busy, like an activity indicator view
[theTokenCharObject getMyTokenChar:@"someParams" completion:^(NSString *tokenChar) {
    // remove the activity indicator view
    // show something new to the user: "we got the thing that depends on tokenChar!!"
}];

有两种选择,最简单的描述是使用NSNotificationCenter。您原始的方法将变得(似乎)同步...

- (void)getMyTokenChar:(id)someParams {
    // form cardParams with someParams (or maybe they are the same
    [sharedClient createTokenWithCard:cardParams completion:^(STPToken *token,NSError *error) {
        NSString *tokenChar;
        if (token == nil || error != nil) {
            tokenChar = [error.localizedDescription UTF8String];
        } else {
            tokenChar = [token.tokenId UTF8String];
        }
        [[NSNotificationCenter defaultCenter] postNotificationName:@"TokenGetterDidGetToken" object:tokenChar];
    }];
}

您应用的其他任何部分都这样订阅...

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didGetToken:) name:@"TokenGetterDidGetToken" object:nil];

并且必须实现选择器...

- (void)didGetToken:(NSNotification *)notification {
    // notification.object will be the tokenChar
}

不过,通常情况下,最好不要传递块。以我给的UI示例为例。代码的一部分将UI更改为繁忙,另一部分未连接的部分将更改为UI。