Вызов завершения C-Objective C никогда не завершается - PullRequest
0 голосов
/ 27 июня 2018

Я пытаюсь отправить экземпляр внутри блокирующего вызова и жду его завершения, чтобы потом можно было использовать это значение в моей программе, но функция завершения никогда не завершается. Я чрезвычайно новичок в Objective-C, и я использую только цель-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 голосов
/ 27 июня 2018

Асинхронные методы имеют способ размножаться, часто заставляя их вызывающих абонентов также быть асинхронными. Другими словами, если результат метода A зависит от метода B, а methodB является асинхронным, то так же должен быть и метод A.

Таким образом, метод, который содержит код 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
}

Как правило, вам будет лучше проходить мимо блоков. Возьмите пример пользовательского интерфейса, который я привел. Одна часть кода изменит пользовательский интерфейс на занятость, другая отключенная часть должна будет вернуть его обратно.

0 голосов
/ 27 июня 2018

Прошло много времени с тех пор, как я обработал target-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
}
...