个人认为,Swift对指针的处理略显复杂。
我们通过调试可以看到存在这样一个类型Builtin.RawPointer,我们可以假设其为C指针在Swife中的内部表示,但不可以直接使用。相反,在Swift中,定义了7种指针类型,可以通过这7种类型来操作C指针。
COpaquePointer
在C语言中,常使用typedef来定义一些指针类型的别名,如
|
|
该定义表示一个指向栈结构的指针,但并没有给出结构的任何信息,我们不知道该结构都有哪些成员。类似于stack_t这样的指针就是一个不透明的指针。在程序中,我们可以自由的操纵这种指针,但无法反引用以获取指针的内部信息,只有接口的实现才有这种特权。这所以使用不透明指针,在于其隐藏了具体的实现细节,有助于捕获错误。即只能传相同的参数给函数,否则将产一变异错误。(参看《C语言接口与实现:创建可重用软件的技术》)
在Swift中,针对这种不透明指针,定义了一个包装器:COpaquePointer。该类型(实际为结构体)是一个不透明指针的包装器,主要用于Bridge Header中表示C中的复杂结构指针,当我们的指针指向的类型无法在Swift中有效地表示出来时,就可以使用该类型,而如果在Swift能找到对应的类型表示指针指向的类型,则可以使用UnsafePointer。
该类的实现及其扩展实现了四个协议,我们看看其具体代码
|
|
可以看到该结构体并没有做太多的事情,仅仅实现了四个协议的接口,同时做了个UnsafePointer类型的转换操作。该类型在Swift使用得比较多,例如在NSData中,bytes属性的类型就是COpaquePointer,而该属性在Objective-C中的类型是const void*。
UnsafePointer
UnsafePointer一个泛型结构体,可以说是处理C指针最主要的结构体了。它包装并存储了类型为T的C指针,主要用于与C标准库交互。该类没有提供自动管理内存功能,所以我们在使用的时候需要注意内存的分配与释放。当在Swift能找到与T类型相对应的类型时,可以使用该类型,否则就考虑使用COpaquePointer。
在该类型的声明中,我们可以看到它定义了大量的方法来处理指针,具体如下
|
|
UnsafePointer提供了多个构造器,以从其它指针类型创建一个UnsafePointer。另外,CMutablePointer、CMutableVoidPointer、CConstPointer、CConstVoidPointer都提供了一个withUnsafePointer方法,该方法是让这些指针类型在一个闭包中可以像UnsafePointer类型一样使用。我们来看一个例子:
|
|
目前感觉在通常情况下,UnsafePointer使用得不多,平时接触到的更多的是CMutablePointer、CMutableVoidPointer、CConstPointer、CConstVoidPointer、AutoreleasingUnsafePointer这五种指针类型,这五种指针的基本使用方法我们在Swift中C指针的基本使用方法有简单介绍过,下面我们也讲讲它们的基本实现。
AutoreleasingUnsafePointer
该类型的基本定义是一个指向Objective-C指针的可变指针。这个类型有几种隐式转换来允许传递下面几种参数类型给一个一个C或ObjC API:
- nil, 作为null指针传入
- 被引用类型的in-out参数,作为一个带有自动释放所有权语义的可回写变量的指针传递
- UnsafePointer
与CMutablePointer不同的是,Swift不直接支持传递一个指向元素是Objc类指针数组的指针。与UnsafePointer
该类型不像其它的C*Pointer类型一样携带一个所有者指针,因为它只需要引用in-out参数转换的结果,而这个参数已经有一个回写域的生命周期了。
我们来看看其声明:
|
|
CConstPointer与CConstVoidPointer
这两个类型都是C常量指针,分别对应于C中的const T和const void。它们没有自己的操作。它的值由owner-value对组成,也正因此,它不能直接传递给C函数。在桥接的过程中,它会维护一个owner的强引用,且指针值被传递到C或者Objective-C的入口点。这允许拥有堆存储的类型(如数组)将自己转换为一个指针,同时仍保证在调用的过程中仍然维护它们的存储。我们可以看下它们的具体实现
|
|
CMutablePointer与CMutableVoidPointer
这两个类型都是C可变指针,分别对应于C中的T 和void ,其基本内容同上。
总结
由上面的分析,依据类型是否可以直接用于C函数声明,可以将这7种指针类型分为两类:
- 可直接用于C函数声明:COpaquePointer,UnsafePointer
,AutoreleasingUnsafePointer ,它们是对Builtin.RawPointer的封装,直接对应于C指针,其sizeof是单位字长 - 不可直接用于C函数声明:CMutablePointer
, CConstPointer , CMutableVoidPointer, CConstVoidPointer。这几个类型都有一个owner属性用于管理实例的生命周期,可以直接从Swift对象的引用获得,另外还可以使用withUnsafePointer方法,该方法是让这些指针类型在一个闭包中可以像UnsafePointer类型一样使用。
另外有两点需要注意
- 非常量指针都实现了LogicValue协议,因此可以直接使用if a_pointer来判断其是否为NULL。
- nil类型实现了到所有指针类型的隐式类型转换,等价于C中的NULL,可以直接判断。
至于这些指针的使用,需要在根据实现情况来具体处理。
参考
- 《C语言接口与实现:创建可重用软件的技术》
- Swift.String to CMutablePointer
- Swift and C Interop Cont. (简析 Swift 和 C 的交互,Part 二)