Swift 3.0在2.0基础上做了许多改动,其中之一就是与C API的兼容性上。这里我将主要讲解一下C语言的指针与Swift编程语言的桥接在Swift 3.0中改成啥样了。
首先,为了Swift编程语言语法体系的统一性,Swift语言核心设计团队为所有诸如UnsafePointer、UnsafeMutablePointer等类型增加了Optional,这个在Swift 2.x中是没有的,不过你可以直接对一个UnsafePointer<Int32>类型的对象置空。而在3.0版本中增加了这些类型的Optional属性之后,我们就可以把它们作为其他类型一样去对待了。
然后,Swift 3.0给void*和const void*分别引入了UnsafeRawPointer类型和UnsafeMutableRawPointer类型。而在Swift 2.x中,它们分别对应的是UnsafePointer<Void>与UnsafeMutablePointer<Void>。此外,UnsafeRawPointer类型与UnsafeMutableRawPointer类型不能直接通过UnsafePointer与UnsafeMutablePointer的构造器转换为相应类型,而只能通过它们的assumingMemoryBound(to:)方法去转。
最后,UnsafePointer类型要转为UnsafeMutablePointer类型时现在必须使用UnsafeMutablePointer的init(mutating:)方法,这里增加了一个参数标签mutating。
下面我们将通过一段代码来呈现以上说的这三点。以下有3个代码片段,分别是一个头文件、一个C源文件和一个Swift源文件。
头文件内容:
extern void* _Nonnull GenerateData(void);
extern void UseData(const int* _Nonnull pData);
extern const void* _Nullable GenerateData2(void);
extern void UseData2(int* _Nullable pData);
C源文件内容:
#include <stdio.h>
static int sData = 10;
void* GenerateData(void)
{
return &sData;
}
void UseData(const int *pData)
{
printf("The data is: %d\n", *pData);
}
const void* _Nullable GenerateData2(void)
{
return &sData;
}
void UseData2(int *pData)
{
if(pData != NULL)
printf("The data is: %d\n", pData[0]);
}
Swift源文件内容:
class ViewController: NSViewController {
override func viewDidLoad() {
super.viewDidLoad()
let dataPtr = GenerateData()
// 这里可以看到,dataPtr的类型是UnsafeMutableRawPointer
print("dataPtr type is: \(type(of: dataPtr))")
// 这里先将dataPtr类型转换为UnsafeMutablePointer<Int32>类型
let dataInt32Ptr = dataPtr.assumingMemoryBound(to: type(of: Int32()))
print("dataInt32Ptr type is: \(type(of: dataInt32Ptr))")
// 我们还可以对指针所指向的整型数据做些修改
dataInt32Ptr.pointee += 10
// 在传递UseData的实参时,需要将dataInt32Ptr的类型再转为UnsafePointer<Int32>
UseData(UnsafePointer<Int32>(dataInt32Ptr))
// 这里dataPtr2是UnsafeRawPointer?类型
let dataPtr2 = GenerateData2()
// 这里dataInt32Ptr2的类型是UnsafePointer<Int32>?
let dataInt32Ptr2 = dataPtr2?.assumingMemoryBound(to: type(of: Int32()))
// 这里需要将dataInt32Ptr2类型转换为UnsafeMutablePointer<Int32>
UseData2(UnsafeMutablePointer<Int32>(mutating: dataInt32Ptr2))
var intObj: Int32 = 0
// 这里可以看到,在Swift中的一个Int32类型对象,
// 对它取地址操作也可以与UnsafePointer<Int32>类型进行匹配
UseData(&intObj)
UseData2(&intObj)
var uintObj: UInt = 1
// 如果要将一个UnsafePointer<UInt>转换为UnsafePointer<Int32>,
// 现在无法直接用UnsafePointer的构造方法进行转换。
// 为了看清整个转换过程,我们先用withUnsafePointer来获取uintObj的指针类型对象
let uintPtr = withUnsafePointer(to: &uintObj) {
(ptr: UnsafePointer<UInt>) -> UnsafePointer<UInt> in
return ptr
}
// 这里使用了Swift 3新引入的UnsafePointer与UnsafeMutablePointer的
// withMemoryRebound(to:capacity:_:)方法显式地将当前指针的原始类型
// 转换为目标类型的指针对象。
// 这里的Int32.self相当于type(of: Int32()),获取到的是Int32元类型
UseData(uintPtr.withMemoryRebound(to: Int32.self, capacity: 1) {
(ptr: UnsafePointer<Int32>) -> UnsafePointer<Int32> in
return ptr
})
// 为了看清下一步操作,我们这里将withMemoryRebound方法所返回的
// UnsafePointer<Int32>对象hold住
let constPtr = uintPtr.withMemoryRebound(to: Int32.self, capacity: 1) {
(ptr: UnsafePointer<Int32>) -> UnsafePointer<Int32> in
return ptr
}
}
}
上述代码详细介绍了如何通过C语言函数接口获得一个指针类型,然后做相互转换。此外,还有在Swift中定义的对象如何作为指针类型的参数传入C语言函数中。这里涉及到了Swift 3.0中的新方法,包括UnsafeRawPointer与UnsafeMutableRawPointer的assumingMemoryBound(to:)方法;UnsafePointer与UnsafeMutablePointer的withMemoryRebound(to:capacity:_:)方法。Swift 3.0中取消了UnsafePointer与UnsafeMutablePointer构造方法对任意类型的指针进行转换的实现,取而代之的是,对于指针转换的数据类型都一致的情况,可以通过UnsafeMutablePointer中的init(mutating:)方法将UnsafePointer转换UnsafeMutablePointer;而UnsafePointer可直接使用init(_:)将UnsafeMutablePointer转换相应类型的UnsafePointer类型。除此之外,非相应数据类型的指针转换都必须使用withMemoryRebound(to:capacity:_:)方法。
各位可以在Xcode 8上尝试运行。