Ios 目标C完成呼叫从未完成

Ios 目标C完成呼叫从未完成,ios,objective-c,completionhandler,Ios,Objective C,Completionhandler,我试图在阻塞调用中发送一个实例,并等待它完成,以便稍后在程序中使用该值,但完成函数永远不会完成。我对Objective-C非常陌生,我只使用Objective-C作为包装器,我不明白为什么我的完成调用永远不会完成 [sharedClient createTokenWithCard:cardParams completion:^(STPToken *token,NSError *error) { if (token == nil || error != nil) { tok

我试图在阻塞调用中发送一个实例,并等待它完成,以便稍后在程序中使用该值,但完成函数永远不会完成。我对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);
    }];
}

我已经有一段时间没有处理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
}

异步方法有一种扩散的方式,通常迫使它们的调用者也是异步的。换句话说,如果methodA的结果依赖于methodB,并且methodB是异步的,那么methodA也必须是异步的

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

- (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
}];
坏消息是,包含调用代码的方法可能也需要异步。它永远不会结束吗

是的,它结束了,通常在UI

// 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更改为繁忙,另一部分断开连接的部分将不得不将其更改回繁忙状态

不要那样做。使用完成块中的值。您将阻塞主线程,这将使您的应用程序无响应。在处理此场景时,我可以考虑两种方法。1) 调度组等待。2) 制作一个处理回调的函数,并从createTOkenWithCard方法调用整个函数的完成。要确定为什么从不调用完成,我们需要使用
createTOkenWithCard
的代码,因为那是负责呼叫完成区的人。我看到我们在同一时间回答了几乎相同的问题。我将离开我的,因为它在两个方面是不同的:(a)OP在出现错误的情况下用错误重载tokenChar,(b)我添加NSNotification作为替代方法。(KVO也是一种选择)。次要的一点,someFunctionName需要声明
cardParams
tokenChar
来编译。是的,我从来没有想过通知,它仍然是一种很好的选择,我认为这是一个很好的选择,在card param上也是一个很好的选择,从来没有注意到它。谢谢您的回复。对不起,我是全新的,Obj-C看起来和C++和java有很大的不同。我的格式应该是这样的吗?(见上图)我想可能会遇到问题,因为我正在通过cython运行这个包装器。我觉得我将要做无休止的完成积木。如果我通过cython调用,最后一个完成块就足够了吗?格式是一个品味问题。我的观点被普遍接受(尽管有些地方有点特殊,例如许多作者说
SomeClass*
,我说
SomeClass*
)。我从未使用过cython,但这取决于python调用程序是否需要返回给它的异步方法的结果。如果是这样,那么整个链需要是异步的。