簡體   English   中英

從 Swift 傳遞回調函數調用 Objective C 和 C

[英]calling Objective C and C from Swift passing callback function

我正在嘗試從 Swift 調用 HappyTime onvif 庫。

我將庫鏈接到我的項目,並且我能夠調用一些簡單的函數,但是我無法在傳遞我的回調函數的調用中正確獲取語法。

這是斯威夫特代碼:

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

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

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

我的setProbeCB調用給出了這個錯誤:

無法將 '(UnsafeMutablePointer) -> ()' 類型的值轉換為預期的參數類型 'UnsafeMutablePointer'(又名 'UnsafeMutablePointer, UnsafeMutablePointer<()>) -> ()>>')

這是 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;

應該修復的一件事是回調的參數數量不匹配。 迅速調用目標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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM