从Swift调用Objective C和C传递回调函数

时间:2016-04-01 14:18:51

标签: objective-c c swift bridging-header

我试图从Swift调用HappyTime onvif库。

我已将库链接到我的项目中,并且我可以调用一些简单的函数,但是我在通过调用函数的调用中无法正确获取语法。

这是Swift代码:

func discoverCameras()
{
    HappyInterface.sharedInstance().startProb()

//this line gives syntax error
    HappyInterface.sharedInstance().setProbeCB(cameraDiscovered)
}

func cameraDiscovered(cameraFound:UnsafeMutablePointer<DEVICE_BINFO>)
{
    table.reloadData()
}

我的setProbeCB调用给出了此错误:

  

无法转换类型'(UnsafeMutablePointer) - &gt;的值()'到期望的参数类型'UnsafeMutablePointer'(又名'UnsafeMutablePointer,UnsafeMutablePointer&lt;()&gt;) - &gt; ()&GT;&GT;')

以下是Obj C的实施:

- (void) setProbeCB:(onvif_probe_cb *)cb {
    set_probe_cb(*cb, 0);

}

这是Obj C标题:

- (void) setProbeCB:(onvif_probe_cb *)cb;

这是C标题:

#ifndef __H_ONVIF_PROBE_H__
#define __H_ONVIF_PROBE_H__

#include "onvif.h"


typedef void (* onvif_probe_cb)(DEVICE_BINFO * p_res, void * pdata);

#ifdef __cplusplus
extern "C" {
#endif

ONVIF_API void set_probe_cb(onvif_probe_cb cb, void * pdata);
ONVIF_API void set_probe_interval(int interval);
ONVIF_API int  start_probe(int interval);
ONVIF_API void stop_probe();
ONVIF_API void send_probe_req();


#ifdef __cplusplus
}
#endif

#endif  //  __H_ONVIF_PROBE_H__

这是C代码:

/***************************************************************************************/
#define MAX_PROBE_FD    8


/***************************************************************************************/
onvif_probe_cb g_probe_cb = 0;
void * g_probe_cb_data = 0;
pthread_t g_probe_thread = 0;
int g_probe_fd[MAX_PROBE_FD];
int g_probe_interval = 30;
BOOL g_probe_running = FALSE;


/***************************************************************************************/
int onvif_probe_init(unsigned int ip)
{   
    int opt = 1;
    SOCKET fd;
    struct sockaddr_in addr;
    struct ip_mreq mcast;

    fd = socket(AF_INET, SOCK_DGRAM, 0);
    if(fd < 0)
    {
        log_print(LOG_ERR, "socket SOCK_DGRAM error!\n");
        return -1;
    }

    addr.sin_family = AF_INET;
    addr.sin_port = htons(3702);
    addr.sin_addr.s_addr = ip;

    if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) == -1)
    {
        // if port 3702 already occupied, only receive unicast message
        addr.sin_port = 0;
        if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) == -1)
        {
            closesocket(fd);
            log_print(LOG_ERR, "bind error! %s\n", sys_os_get_socket_error());
            return -1;
        }
    }

    /* reuse socket addr */  
    if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&opt, sizeof(opt))) 
    {  
        log_print(LOG_WARN, "setsockopt SO_REUSEADDR error!\n");
    }

    memset(&mcast, 0, sizeof(mcast));
    mcast.imr_multiaddr.s_addr = inet_addr("239.255.255.250");
    mcast.imr_interface.s_addr = ip;

    if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&mcast, sizeof(mcast)) < 0)
    {
#if __WIN32_OS__
        if(setsockopt(fd, IPPROTO_IP, 5, (char*)&mcast, sizeof(mcast)) < 0)
#endif      
        {
            closesocket(fd);
            log_print(LOG_ERR, "setsockopt IP_ADD_MEMBERSHIP error! %s\n", sys_os_get_socket_error());
            return -1;
        }
    }

    return fd;
}

char probe_req1[] = 
    "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
    "<Envelope xmlns:tds=\"http://www.onvif.org/ver10/device/wsdl\" xmlns=\"http://www.w3.org/2003/05/soap-envelope\">"
    "<Header>"
    "<wsa:MessageID xmlns:wsa=\"http://schemas.xmlsoap.org/ws/2004/08/addressing\">uuid:%s</wsa:MessageID>"
    "<wsa:To xmlns:wsa=\"http://schemas.xmlsoap.org/ws/2004/08/addressing\">urn:schemas-xmlsoap-org:ws:2005:04:discovery</wsa:To>"
    "<wsa:Action xmlns:wsa=\"http://schemas.xmlsoap.org/ws/2004/08/addressing\">http://schemas.xmlsoap.org/ws/2005/04/discovery/Probe</wsa:Action>"
    "</Header>"
    "<Body>"
    "<Probe xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns=\"http://schemas.xmlsoap.org/ws/2005/04/discovery\">"
    "<Types>tds:Device</Types>"
    "<Scopes />"
    "</Probe>"
    "</Body>"
    "</Envelope>";  

char probe_req2[] = 
    "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
    "<Envelope xmlns:dn=\"http://www.onvif.org/ver10/network/wsdl\" xmlns=\"http://www.w3.org/2003/05/soap-envelope\">"
    "<Header>"
    "<wsa:MessageID xmlns:wsa=\"http://schemas.xmlsoap.org/ws/2004/08/addressing\">uuid:%s</wsa:MessageID>"
    "<wsa:To xmlns:wsa=\"http://schemas.xmlsoap.org/ws/2004/08/addressing\">urn:schemas-xmlsoap-org:ws:2005:04:discovery</wsa:To>"
    "<wsa:Action xmlns:wsa=\"http://schemas.xmlsoap.org/ws/2004/08/addressing\">http://schemas.xmlsoap.org/ws/2005/04/discovery/Probe</wsa:Action>"
    "</Header>"
    "<Body>"
    "<Probe xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns=\"http://schemas.xmlsoap.org/ws/2005/04/discovery\">"
    "<Types>dn:NetworkVideoTransmitter</Types>"
    "<Scopes />"
    "</Probe>"
    "</Body>"
    "</Envelope>";

int onvif_probe_req_tx(int fd)
{
    int len;
    int rlen;
    char  * p_bufs = NULL;
    struct sockaddr_in addr;

    int buflen = 10*1024;

    p_bufs = (char *)malloc(buflen);
    if (NULL == p_bufs)
    {
        return -1;
    }

    memset(p_bufs, 0, buflen);
    sprintf(p_bufs, probe_req1, onvif_uuid_create());

    memset(&addr, 0, sizeof(addr));

    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = inet_addr("239.255.255.250");
    addr.sin_port = htons(3702);

    len = strlen(p_bufs);
    rlen = sendto(fd, p_bufs, len, 0, (struct sockaddr *)&addr, sizeof(struct sockaddr_in));
    if (rlen != len)
    {
        log_print(LOG_ERR, "onvif_probe_req_tx::rlen = %d,slen = %d\r\n", rlen, len);
    }

    usleep(1000);

    memset(p_bufs, 0, buflen);
    sprintf(p_bufs, probe_req2, onvif_uuid_create());

    len = strlen(p_bufs);
    rlen = sendto(fd, p_bufs, len, 0, (struct sockaddr *)&addr, sizeof(struct sockaddr_in));
    if (rlen != len)
    {
        log_print(LOG_ERR, "onvif_probe_req_tx::rlen = %d,slen = %d\r\n", rlen, len);
    }   

    free(p_bufs);

    return rlen;
}

BOOL onvif_parse_device_binfo(XMLN * p_node, DEVICE_BINFO * p_res)
{
    XMLN * p_EndpointReference;
    XMLN * p_Types;
    XMLN * p_XAddrs;

    p_EndpointReference = xml_node_soap_get(p_node, "EndpointReference");
    if (p_EndpointReference)
    {
        XMLN * p_Address = xml_node_soap_get(p_EndpointReference, "Address");
        if (p_Address && p_Address->data)
        {
            strncpy(p_res->EndpointReference, p_Address->data, sizeof(p_res->EndpointReference)-1);
        }
    }

    p_Types = xml_node_soap_get(p_node, "Types");
    if (p_Types && p_Types->data)
    {
        p_res->type = parse_DeviceType(p_Types->data);
    }

    p_XAddrs = xml_node_soap_get(p_node, "XAddrs");
    if (p_XAddrs && p_XAddrs->data)
    {
        parse_XAddr(p_XAddrs->data, &p_res->XAddr);

        if (p_res->XAddr.host[0] == '\0' || p_res->XAddr.port == 0)
        {
            return FALSE;
        }
    }
    else
    {
        return FALSE;
    }   

    return TRUE;
}

BOOL onvif_probe_res(XMLN * p_node, DEVICE_BINFO * p_res)
{
    XMLN * p_body = xml_node_soap_get(p_node, "Body");
    if (p_body)
    {
        XMLN * p_ProbeMatches = xml_node_soap_get(p_body, "ProbeMatches");
        if (p_ProbeMatches)
        {
            XMLN * p_ProbeMatch = xml_node_soap_get(p_ProbeMatches, "ProbeMatch");
            while (p_ProbeMatch && soap_strcmp(p_ProbeMatch->name, "ProbeMatch") == 0)
            {
                if (onvif_parse_device_binfo(p_ProbeMatch, p_res))
                {
                    if (g_probe_cb)
                    {
                        g_probe_cb(p_res, g_probe_cb_data);
                    }
                }

                p_ProbeMatch = p_ProbeMatch->next;
            }
        }
        else
        {
            XMLN * p_Hello = xml_node_soap_get(p_body, "Hello");
            if (p_Hello)
            {   
                if (onvif_parse_device_binfo(p_Hello, p_res))
                {
                    if (g_probe_cb)
                    {
                        g_probe_cb(p_res, g_probe_cb_data);
                    }
                }
            }
        }
    }

    return TRUE;
}

int onvif_probe_net_rx()
{
    int i;
    int ret;
    int maxfd = 0;
    int fd = 0;
    char rbuf[10*1024];
    fd_set fdread;
    struct timeval tv = {1, 0};

    FD_ZERO(&fdread);

    for (i = 0; i < MAX_PROBE_FD; i++)
    {
        if (g_probe_fd[i] > 0)
        {
            FD_SET(g_probe_fd[i], &fdread); 

            if (g_probe_fd[i] > maxfd)
            {
                maxfd = g_probe_fd[i];
            }
        }
    }

    ret = select(maxfd+1, &fdread, NULL, NULL, &tv); 
    if (ret == 0) // Time expired 
    { 
        return 0; 
    }

    for (i = 0; i < MAX_PROBE_FD; i++)
    {
        if (g_probe_fd[i] > 0 && FD_ISSET(g_probe_fd[i], &fdread))
        {
            int rlen;
            int addr_len;
            struct sockaddr_in addr;
            unsigned int src_ip;
            unsigned int src_port;
            XMLN * p_node;

            fd = g_probe_fd[i];

            addr_len = sizeof(struct sockaddr_in);
            rlen = recvfrom(fd, rbuf, sizeof(rbuf), 0, (struct sockaddr *)&addr, (socklen_t*)&addr_len);
            if (rlen <= 0)
            {
                log_print(LOG_ERR, "onvif_probe_net_rx::rlen = %d, fd = %d\r\n", rlen, fd);
                continue;
            }

            src_ip = addr.sin_addr.s_addr;
            src_port = addr.sin_port;

            p_node = xxx_hxml_parse(rbuf, rlen);
            if (p_node == NULL)
            {
                log_print(LOG_ERR, "onvif_probe_net_rx::hxml parse err!!!\r\n");
            }   
            else
            {
                DEVICE_BINFO res;
                memset(&res, 0, sizeof(DEVICE_BINFO));

                onvif_probe_res(p_node, &res);      
            }

            xml_node_del(p_node);
        }
    }

    return 1;
}

void * onvif_probe_thread(void * argv)
{
    int count = 0;

    int i = 0;
    int j = 0;

    for (; i < get_if_nums() && j < MAX_PROBE_FD; i++, j++)
    {
        unsigned int ip = get_if_ip(i);     
        if (ip != 0 && ip != inet_addr("127.0.0.1"))
        {
            g_probe_fd[j] = onvif_probe_init(ip);
        }
    }

    for (i = 0; i < MAX_PROBE_FD; i++)
    {
        if (g_probe_fd[i] > 0)
        {
            onvif_probe_req_tx(g_probe_fd[i]);  
        }
    }

    while (g_probe_running)
    {
        if (onvif_probe_net_rx() == 0)
        {
            count++;
        }

        if (count >= g_probe_interval)
        {
            count = 0;

            for (i = 0; i < MAX_PROBE_FD; i++)
            {
                if (g_probe_fd[i] > 0)
                {
                    onvif_probe_req_tx(g_probe_fd[i]);  
                }
            }       
        }

        usleep(1000);
    }

    g_probe_thread = 0;

    return NULL;
}

ONVIF_API void set_probe_cb(onvif_probe_cb cb, void * pdata)
{
    g_probe_cb = cb;
    g_probe_cb_data = pdata;
}


ONVIF_API void send_probe_req()
{
    int i;  
    for (i = 0; i < MAX_PROBE_FD; i++)
    {
        if (g_probe_fd[i] > 0)
        {
            onvif_probe_req_tx(g_probe_fd[i]);  
        }
    }
}

ONVIF_API void set_probe_interval(int interval)
{
    g_probe_interval = interval;

    if (g_probe_interval < 10)
    {
        g_probe_interval = 30;
    }
}

ONVIF_API int start_probe(int interval)
{
    g_probe_running = TRUE;

    set_probe_interval(interval);

    g_probe_thread = sys_os_create_thread((void *)onvif_probe_thread, NULL);
    if (g_probe_thread)
    {
        return 0;
    }

    return -1;
}

ONVIF_API void stop_probe()
{
    int i;

    g_probe_running = FALSE;

    while (g_probe_thread)
    {
        usleep(1000);
    }

    for (i = 0; i < MAX_PROBE_FD; i++)
    {
        if (g_probe_fd[i] > 0)
        {
            closesocket(g_probe_fd[i]);
            g_probe_fd[i] = 0;
        }
    }
}

这是DEVICE_BINFO结构的样子:

typedef struct
{
    int     type;                               // device type
    char    EndpointReference[100];

    onvif_XAddr XAddr;                          // xaddr, include port host, url
} DEVICE_BINFO;

1 个答案:

答案 0 :(得分:0)

应该修复的一件事是回调的参数数量不匹配。 Swift调用Objective-C setProbeCB()方法,给它一个指向cameraDiscovered()函数的指针,该函数接受单个参数。然后setProbeCB()给出了C set_probe_cb()函数的函数指针,该函数指向一个指向两个参数的函数的指针。

另一个观察结果是setProbeCB()只能取onvif_probe_cb而非onvif_probe_cb*,然后将C代码简称为set_probe_cb(cb, 0)。但是,我认为这并没有多大区别。

另外,我认为这个问题可以提炼到更小的尺寸。

以下是基于原始代码的简化示例。它展示了如何在Swift中实现回调并让C代码调用它,但是当通过回调参数和返回值传递数据时,真正的乐趣就开始了。它变得非常棘手,这就是为什么该示例没有说明如何在Swift代码中处理DEVICE_BINFO。这本身就是一个话题。

在Swift中使用(Objective-)C函数和类型的线索正在弄清楚它们是如何导入Swift的。例如,要了解如何导入onvif_probe_cb,请在Swift代码中的一行中键入它,将光标放在其中,快速帮助将向您显示:

Declaration: typealias onvif_probe_cb = (UnsafeMutablePointer<DEVICE_BINFO>, UnsafeMutablePointer<Void>) -> Void
Declared in: clib.h

它告诉我们在Swift实现的回调中使用的参数和返回类型。 这个例子绝对不是生产质量:在内存管理等方面,各种各样的东西都可能出现问题。请参阅代码注释以获取更多信息。

首先,这是C代码标题(clib.h):

#ifndef clib_h
#define clib_h

#include <stdio.h>

typedef struct {
    char hostname[50];
    int32_t port;
    char url[200];
} onvif_XAddr;

typedef struct
{
    int     type;                               // device type
    char    EndpointReference[100];

    onvif_XAddr XAddr;                          // xaddr, include port host, url
} DEVICE_BINFO;

/** 
 * This is the typedef of the function pointer to be used for our callback.
 * The function takes a pointer to DEVICE_BINFO and a pointer to some arbitrary
 * data meaningful to the code that provides the callback implementation.  It will
 * be NULL in this example.
 */
typedef void (* onvif_probe_cb)(DEVICE_BINFO * p_res, void * pdata);

/**
 * A function to set the callback.
 */
void set_probe_cb(onvif_probe_cb cb, void * pdata);

/**
 * This is a function that calls the callback.
 */
void find_device();

#endif /* clib_h */

以下是我们的C源代码(clib.c):

#include "clib.h"
#include <string.h>

onvif_probe_cb gCB = 0;   // global variable to store the callback pointer
void * gUserData = 0;     // global variable to store pointer to user data
DEVICE_BINFO gDeviceInfo; // global variable to store device info struct

void find_device() {
    // Set up gDeviceInfo
    gDeviceInfo.XAddr.port = 1234;
    strcpy( gDeviceInfo.XAddr.hostname, "myhost");
    strcpy( gDeviceInfo.XAddr.url, "http://junk.com");
    gDeviceInfo.type = 777;
    // ... and, if a callback is available, call it with the device info
    if (gCB) gCB(&gDeviceInfo, gUserData);
    else puts("No callback available");
}

void set_probe_cb(onvif_probe_cb cb, void * pdata) {
    gCB = cb;
    gUserData = pdata;
}

这是Objective-C包装器标头(oclib.h):

#ifndef oclib_h
#define oclib_h

#import "clib.h"
#import <Foundation/Foundation.h>

/**
 * Interface of an Objective-C wrapper around C code in clib.*. We could have
 * gone straight to C from Swift, but I'm trying to keep the example close to the
 * code in the question.  Also, this extra Objective C layer could be helpful in
 * translating data structures, such as DEVICE_BINFO, between C and Swift, since 
 * Objective-C plays much nicer with C data types.  This is no surprise: any C code
 * is valid Objective-C (Objective-C is a strict superset of C).
 */
@interface MyWrapper : NSObject

-(id)init;
// Please note: this one takes a single argument, while the C function it wraps
// takes 2; see the implementation.
-(void) setProbeCB:(onvif_probe_cb) cb;
-(void) findDevice;

@end

#endif /* oclib_h */

包装器实现(oclib.m):

#import "oclib.h"

/**
 * Implementation of our Objective-C wrapper.
 */
@implementation MyWrapper

-(id)init { return self; }

-(void) setProbeCB:(onvif_probe_cb) cb {
    // We don't want anything other than device info to be passed back and
    // forth via the callback, so this wrapper function takes a single argument
    // and passes 0 as the 2nd argument to the wrapped C function.
    set_probe_cb(cb, 0);
}

-(void) findDevice {
    find_device();
}

@end

最后,这是实现回调的Swift代码(main.swift):

var w : MyWrapper = MyWrapper()

/**
 * This is the callback implementation in Swift.  We don't use the 2nd argument, userData, but it still
 * has to be present to satisfy the way the callback function pointer is specified in C code.
 */
func cameraDiscovered( info : UnsafeMutablePointer<DEVICE_BINFO>, userData : UnsafeMutablePointer<Void>) {
    print("Called the Swift callback!")

    let devInfo : DEVICE_BINFO = info.memory;
    print( "The device type is \(devInfo.type)")
    print( "The device port is \(devInfo.XAddr.port)")
}

// Provide the callback to C code via Objective-C
w.setProbeCB(cameraDiscovered)

// ... and call a function that will cause the C code to invoke the callback.
w.findDevice()

桥接头只有#import oclib.h,因此将C和Objective-C头的内容暴露给Swift。

预期产出:

Called the Swift callback!
The device type is 777
The device port is 1234