Ios 目标C完成呼叫从未完成
我试图在阻塞调用中发送一个实例,并等待它完成,以便稍后在程序中使用该值,但完成函数永远不会完成。我对Objective-C非常陌生,我只使用Objective-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
[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调用程序是否需要返回给它的异步方法的结果。如果是这样,那么整个链需要是异步的。