Запуск функции обратного вызова C внутри блока завершения Objective C - PullRequest
0 голосов
/ 29 июня 2018

Я работал над созданием приложения для kivy / python, и мне нужно было вызвать некоторые фреймворки ios с помощью obj-c. Итак, я сделал несколько слоев оберток с помощью python-> cython-> c-> obj-c-> framework. До сих пор у меня есть все, чтобы работать до тех пор, пока функция обратного вызова, которая проходит вплоть до Python. Прямо сейчас обратный вызов не проходит где-то между слоем cython-> C-> obj-c (никогда не попадает в мою печать в cython). Я чувствую себя так, потому что я пытаюсь вызвать user_func как функцию C, а не как функцию obj-c. Как мне запустить функцию обратного вызова C внутри obj-c? Я засыпал код отпечатками (не могу пройтись по тому, как настроены мои настройки), и он печатает сгенерированный токен, а затем вылетает прямо в user_func. Он также никогда не достигает функции обратного вызова в моем файле Cython. Так что где-то между этими двумя источниками происходит сбой.

- (void) retrieveTokenObjC:(char*)myKey andcardNumber:(char*)cardNumber andexpMonth:(int)expMonth andexpYear:(int)expYear andcvc:(char*)cvc anduser_func:(tokenfunc)user_func anduser_data:(void*)user_data {

    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 *apiClient = [[STPAPIClient alloc] initWithPublishableKey:myPublishableKey];

    [apiClient createTokenWithCard:cardParams completion:^(STPToken *token,NSError *error) {

        if (token == nil || error != nil) {
            const char* errorChar = [error.localizedDescription UTF8String];
            user_func(errorChar,user_data);
        } else {
            const char* tokenChar = [token.tokenId UTF8String];
            user_func(tokenChar,user_data);
        }
    }];
}

После этого идет заголовок obj-c

#import <Foundation/Foundation.h>
typedef void (*tokenfunc) (const char *name, void *user_data);

@interface retToken : NSObject
- (void) retrieveTokenObjC:(char*)myKey andcardNumber:(char*)cardNumber andexpMonth:(int)expMonth andexpYear:(int)expYear andcvc:(char*)cvc anduser_func:(tokenfunc)user_func anduser_data:(void*)user_data;
@end

Затем он входит в оболочку c для Cython.

#include "stripe_ios_c.h"
#include "stripe_ios_imp.h"

    void retrieveToken(char* myKey, char* cardNumber, int expMonth, int expYear, char* cvc,tokenfunc user_func, void *user_data){
        retToken* retrieveToken = [[retToken alloc] init];
        [retrieveToken retrieveTokenObjC:myKey andcardNumber:cardNumber andexpMonth:expMonth andexpYear:expYear andcvc:cvc anduser_func:user_func anduser_data:user_data];
    }

Затем файл заголовка для оболочки c

typedef void (*tokenfunc)(const char *name, void *user_data);
void retrieveToken(char* myKey, char* cardNumber, int expMonth, int expYear, char* cvc,tokenfunc user_func, void *user_data);

И, наконец, код Cython

__all__ = ['StripeWrapper']

cdef extern from "stripe_ios_c.h":
    ctypedef void (*tokenfunc)(const char *name, void *user_data)
    void retrieveToken(char* myKey, char* cardNumber, int expMonth, int expYear, char* cvc,tokenfunc user_func, void *user_data)

class StripeWrapper():

    def __init__(self,**kwargs):
        foo = 'bar'
        pass

    def getToken(self,tokenCallback,myKey,cardNumber,expMonth,expYear,cvc):

        cdef bytes myKey_bytes = myKey.encode('utf-8')
        cdef char* myKey_string = myKey_bytes
        cdef bytes cardNumber_bytes = cardNumber.encode('utf-8')
        cdef char* cardNumber_string = cardNumber_bytes
        cdef bytes cvc_bytes = cvc.encode('utf-8')
        cdef char* cvc_string = cvc_bytes

        print myKey_bytes
        print cardNumber_bytes
        print cvc_bytes
        print myKey_string
        print cardNumber_string
        print cvc_string

        retrieveToken(myKey_bytes, cardNumber_bytes, expMonth, expYear, cvc_bytes, callback, <void*>tokenCallback)
        print 'Debug 1'

cdef void callback(const char *name, void *tokenCallback):
    print 'callback debug'
    (<object>tokenCallback)(name.decode('utf-8'))

Обновление: я отследил проблему, и моя функция обратного вызова выполняет проблему, когда обратный вызов python освобождается где-то по пути.

1 Ответ

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

Я решил проблему. Пример здесь https://github.com/cython/cython/blob/master/Demos/callback/run_cheese.py для обратного вызова Cython не будет работать, если вы оставите основной / текущий файл. Это потому, что в тот момент, когда вы покидаете этот файл, память освобождается. После нажатия на объект Python и использования

cdef void callback(const char *name, void *tokenCallback):
    (<object> tokenCallback).token = (name.decode('utf-8'))

Мне кажется, что Cython - это пример плохого примера, и он должен был использовать объект для отправки обратного вызова, что могло бы предотвратить большое разочарование, но в конце концов это работает!

...