Swift编程语言是一门灵活、简洁、安全而又强大的编程语言。其简洁性与安全性从它语法设计上就能看出。而它的高效性从它可以提供针对硬件底层进行编程的特性就能看出。尤其是从Swift 2.x中,其指针类型的使用变得更加灵活,甚至可以直接将一个整数作为地址,使得单纯通过Swift本身即可访问MMR寄存器。不过本博文将描述它另一个非常棒的特性——即与C API几乎完美的兼容!
Swift编程语言与C语言的交互同Java、C#还不太一样,由于Swift的运行时非常小,它基本用于作为静态编译的编程语言进行使用,因此最终可以直接生成二进制目标文件与其他编程语言得到的二进制目标文件进行连接。所以它天生就能非常自然地与C语言、Objective-C进行交互使用。关于Swift与C语言指针类型的对应表可参考Apple官方的《Using Swift with Cocoa and Objective-C》文档。
Swift 3.0中引入了UnsafeRawPointer对应于C语言的const void *类型;UnsafeMutableRawPointer对应于void*类型,然后为它提供了显式转换为指向其他类型的指针对象的方法——assumingMemoryBound(to:)。对于指向不同对象类型的指针类型转换提供了withMemoryRebound(to:capacity:_:)方法。这些都可以参考此博文:http://www.jb51.cc/article/p-hjqzrjzn-bpc.html。本博文主要将介绍对于更复杂的多级指针类型,在Swift编程语言中如何体现;倘若我们要将一个Swift函数暴露给C语言使用,涉及到指针类型的话该如何判断。
我们先举一些例子,这里提供了TestPtr1道TestPtr5这5个函数,参数为一个指针对象,这些函数的形参类型由简单到复杂依次推进,所以我们可以更清晰地做类型分析。
extern void TestPtr1(int * _Nonnull); extern void TestPtr2(const int* _Nonnull); extern void TestPtr3(int * _Nonnull * _Nonnull); extern void TestPtr4(const int * _Nonnull * _Nonnull); extern void TestPtr5(int const * _Nonnull const * _Nonnull);上述函数原型声明中,为了简化指针类型的讨论,我们都将它们声明为_Nonnull属性。然后我们看看下面的C源文件对这些函数的实现:
#include <stdio.h> static int s = 100; void TestPtr1(int *p) { *p += 10; printf("This is: %s\n",__func__); } void TestPtr2(const int *p) { printf("This is: %s,*p = %d\n",__func__,*p); } void TestPtr3(int **pp) { *pp = &s; printf("This is: %s\n",__func__); } void TestPtr4(const int **pp) { *pp = &s; printf("This is: %s\n",__func__); } void TestPtr5(int const * const *pp) { printf("This is: %s,**pp = %d\n",**pp); }上述函数的实现都非常简单。因为我们主要关心的指针类型到Swift中所对应的类型。此外,我们可以看到函数形参从p到pp,类型由简单到复杂。
var a: Int32 = 1 // TestPtr1的类型为:(UnsafeMutablePointer<Int32>) -> Void TestPtr1(&a) // TestPtr2的类型为:(UnsafePointer<Int32>) -> Void TestPtr2(&a) // 这里制作一个指向变量a的指针变量 var aptr = withUnsafeMutablePointer(to: &a) { return $0 } // TestPtr3的类型为:(UnsafeMutablePointer<UnsafeMutablePointer<Int32>>) -> Void TestPtr3(&aptr) // 我们这里可以看到,aptr已经被间接修改了,此时它指向了C源文件中定义的静态变量s的地址 print("aptr's content: \(aptr.pointee)") // 我们这里通过aptr对它所指向的静态变量s做间接修改 aptr.pointee += 100 // 我们这里制作一个指向变量a的指针常量 var cptr = withUnsafePointer(to: &a) { return $0 } // TestPtr4的类型为:(UnsafeMutablePointer<UnsafePointer<Int32>>) -> Void TestPtr4(&cptr) // 我们这里可以看到,cptr也被间接修改,指向了C源文件中的静态变量s的地址。 // 同时,我们也可以看到,静态变量s的值变成了200 print("cptr's content: \(cptr[0])") // TestPtr5的类型为:(UnsafePointer<UnsafePointer<Int32>>) -> Void TestPtr5(&cptr)通过强大的Xcode开发环境,它所提供的代码智能感知,我们只要输入某个函数名,那么其参数类型就会被自动展现出来,十分方便!
那么如果我们要实现一个Swift函数,其对应的C语言的指针类型该如何判定呢?其实规则非常简单!下面我将为大家介绍对应方法。
1、首先,在Swift中,一个变量是用var声明的,它对应到C语言中就是类型名,这里用Type表示。而一个常量是用let声明的,对应到C语言中用const Type表示。
2、Swift中引入了UnsafeMutablePointer<T>用于表示一个指针对象,它对应C语言中的 T* 。
3、Swift中引入了UnsafePointer<T>用于表示一个指针对象,并且对该指针所访问的值做了常量限定,因此它对应C语言的 const T * 。
好了,下面我们将根据上述三种类型的对应关系扩展到多级指针的类型对应法则上。首先,对于UnsafeMutablePointer< UnsafeMutablePointer<T> >类型,它对应于C语言的哪种类型就十分容易判定了,首先是最外层的UnsafeMutablePointer表示为 * ,因此,我们将先把 * 写好。然后看它里面的类型,也是UnsafeMutable,那么我们在第一个 * 之前再添加一个 * 。最后,在UnsafeMutablePointer泛型实参中看到的是T,那么我们将T放在最前面,因此得到C语言对应的类型为 T** 。
而对于UnsafeMutablePointer< UnsafePointer<T> >类型也如法炮制:先写好 * ,然后里边是一个UnsafePointer,表示指向一个常量对象类型的指针,因此我们用 const * 来表示,所以在第一个 * 之前添加之后可得到 const * *。而UnsafePointer中的泛型实参为T,因此最后得到C语言类型为 T const * *,通常也表示为const T * *。
而对于UnsafePointer< UnsafeMutablePointer<T> >类型呢?首先,UnsafePointer对应的是指向常量对象的指针,因此我们用 const *来表示。它里面的UnsafeMutablePointer则同样用 * 表示,放到 const *之前,得到 * const *。最后,UnsafeMutablePointer里面的T放到最前面可得:T * const * 。
最后,UnsafePointer< UnsafePointer<T> >则很容易就能判断出来了——最外层的UnsafePointer先写好:const *。然后里面的附加上:const * const *。最后把 T加上,得:T const * const *。整理后,得到:const T * const *。