简体   繁体   中英

Running a C callback function inside an Objective-C completion block

I've been working on making an app with kivy/python and I needed to call some ios frameworks with obj-c. So I've made a couple layers of wrappers with python->cython->c->obj-c->framework. So far I've got everything to work up until the call back funtion that goes all the way back through to python. Right now the call back is failing somewhere between the cython->C->obj-c layer (never hits my print in cython). I feel like its because I'm trying to call user_func as a C function and not like an obj-c function. How should I go about running my C callback func inside of obj-c? I've peppered the code with prints (can't step through the way my setup is) and it prints the token that's generated and then crashes right on the user_func. It also never reaches the callback function in my cython file. So somewhere between the two is the source of the crash.

- (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);
        }
    }];
}

After this it goes the obj-c header

#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

Then it goes into ac wrapper for 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];
    }

Then the header file for the c wrapper

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);

And finally to the cython code

__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'))

Update: I've tracked the issue down and my call back function executes the problem is the python callback is being deallocated somewhere along the way.

I solved the issue. The example here https://github.com/cython/cython/blob/master/Demos/callback/run_cheese.py for a cython callback won't work if you leave the main/current file. This is because the moment you leave that file the memory is deallocated. After pushing a python object and using

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

I feel like the cython is example is kind of a bad example and should have used an object to send the callback with which could have prevented lots of frustration but it finally works!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM