在这一节,我们来分享和函数有关的桥接工作。这是一个有些复杂的过程,根据函数参数和返回值的不同,桥接的方式也有差异。作为第一部分,我们先来看简单函数的桥接过程。所谓简单函数,就是指那些参数和返回值都是简单类型,并且不使用指针的函数。
固定参数个数的函数
固定参数个数的简单函数桥接到Swift时很简单,几乎会“原封不动”的桥接到Swift。例如,在traditional_oc.h中,声明一个全局函数:
int add(int m, int n);
并在traditional_oc.m中定义它:
int add(int m, int n) {
return m + n;
}
在Swift里,add
会变成这样:
func add(_ m: Int32, _ n: Int32) -> Int32 {
return m + n
}
其中有两点需要注意:
- C中桥街过来的函数默认都是省略external name的;
- C中的
int
会自动转换成Int32
,因此默认是不能传递SwiftInt
类型的,只能使用CInt
类型;
不固定参数个数的函数
接下来,我们了解不固定参数个数的函数。在C里,我们有两种方法定义这样的函数,例如,先在traditional_oc.h中添加下面的声明:
int sum(int count, ...);
int vsum(int count, va_list numbers);
这两个函数的第一个参数表示后续不确定参数的个数,然后在traditional_oc.m中实现它们:
int sum(int count, ...) {
va_list ap;
int s = 0;
va_start(ap, count);
vsum(count, ap);
va_end(ap);
return s;
}
int vsum(int count, va_list numbers) {
int s = 0;
int i = 0;
for (; i < count; ++i) {
s += va_arg(numbers, int);
}
return s;
}
它们都是很简单的C代码,如果你还不熟悉C的可变参数函数,可以先在这里简单了解下,我们就不重复了。这两个函数会如何桥接到Swift呢?遗憾的是,Swift只能接受vsum
,而不能接受sum
。也就是说,无论如何都无法在Swift中直接调用sum
函数。而vsum
桥接到Swift之后,是这样的:
func vsum(count: Int32, numbers: CVaListPointer) -> Int32
因此,在Swift里,我们不能像vsum(6, 1, 2, 3, 4, 5, 6)
这样调用vsum
,那么这个CVaListPointer
是什么呢?简单来说,它就是C中va_list
桥接到Swift后对应的类型。为了得到这个对象,我们有两种方法。
第一种,是调用getVaList
方法,并把要传递的可变参数作为一个数组传递给它:
let vaListPointer = getVaList([1, 2, 3, 4, 5, 6])
let sum = vsum(6, vaListPointer)
这样,我们就可以把vaListPointer
作为vsum
的第二个参数了。
第二种,是调用withVaList
方法,它的第一个参数是一个数组,我们像调用getVaList
一样把所有可变参数传递给它;第二个参数是一个closure,withVaList
会根据第一参数中的所有成员,生成一个对应的CVaListPointer
对象,并传递给这个closure。因此,我们只要在closure里调用vsum
就好了:
let sum = withVaList([1, 2, 3, 4, 5, 6]) {
vaListPointer in
vsum(6, vaListPointer)
}
当然,这两种方法的结果,是完全一样的。
What's next?
以上,就是两种简单函数桥接到Swift时的处理方式,在进一步了解C函数和Swift的交互前,还要做不少铺垫工作,因此,我们先把这个话题放放。下一节,我们来看Swift如何处理C中的struct
和union
。