与C语言交互发生在使用一些C语言编写的API上。C语言的语法会桥接到Swift中对应的语法中。Swift能很好地与C语言交互。
类型
C语言的基础类型、枚举、结构体、联合体在Swift中都有对应。
其中基础类型是一一对等的。其命名方式对C中的类型采取驼峰式命名之后,加上前缀字母C。
例如:
int
变成CInt
;unsigned char
变成CUnsignedChar
;unsigned long long
变成CUnsignedLongLong
;
其中,只有有三个表示宽字符的类型是特殊的:
wchat_t
变成CWideChar
;char16_t
变成CChar16
;char32_t
变成CChar32
;
这些在Swift对应的基础类型都是typealias,在Swift应应使用typealias类型而不直接使用原生类型。
变量
全局变量/常量也同样映射到Swift的全局变量/常量中。
枚举、结构体、联合体
C中的枚举只是一个普通的基础常量,除非使用NS_×_ENUM
、NS_OPTIONS
关键字修饰来定义枚举才会映射到Swift中的结构体,但基本使用跟Swift的枚举无异。
结构体则可以直接映射到Swift的结构体。
联合体则是映射到一个结构体中。
函数
C语言的函数基本能映射到Swift的函数中,除了不支持不固定参数函数,作为替代方案,Swift支持映射va_list
表示的可变参数列表。
使用CF_SWIFT_NAME
还能把C函数重命名甚至并入extension中。
指针
Swift的指针都带有Unsafe关键字,表示其使用在编译期可能是不可预测的。
Apple Developer 文档里有 C 指针和 Swift 指针的对应表:
C Syntax | Swift Syntax |
---|---|
const Type * |
UnsafePointer |
Type * |
UnsafeMutablePointer |
Type * const * |
UnsafePointer |
Type * __strong * |
UnsafeMutablePointer |
Type ** |
AutoreleasingUnsafeMutablePointer |
const void * |
UnsafeRawPointer |
void * |
UnsafeMutableRawPointer |
除此以外,Swift还有几种指针表达方式:
- UnsafeBufferPointer、UnsafeMutableBufferPointer、UnsafeRawBufferPointer、UnsafeMutableRawBufferPointer
- OpaquePointer
Buffer
它相当于在原始内存空间上添加一个view,以步幅(MemoryLayout<T>.stride
)单位,以集合的方式访问底层内存。对应C语言的就是数组的访问。
OpaquePointer
对于在C回调函数中要传递Swift的对象时,这些对象可能无法桥接到C的类型,这时就要用到OpaquePointer。
转换Swift对象为指针需要用到Unmanaged。
1 | var fooObj = Foo() |
Unmanaged创建和转换都有Retained和Unretained版本,对应的是指是否引用计数操作。Pass方法是否+1引用计数;take方法是否-1引用计数。
字符串指针
C中表示字符串的 char *
,桥接到Swift会变成 UnsafeMutablePointer<Int8>
,可以通过相应的构造方法创建String类型。
指针转换
方法参数是指针
调用将指针作为参数的函数时,可以使用隐式转换来传递兼容的指针类型或使用隐式桥接来传递指向变量或数组内容的指针。
若是常量指针参数(UnsafePointer<Type>
),可以直接传递:字符串、指定类型数组、指定类型的inout表达式。
若是可变指针参数(UnsafeMutablePointer<Type>
),可以直接传递:指定类型数组的inout表达式、指定类型的inout表达式。
向下隐式转换
指针在作为函数参数传递时,可以隐式转换:
- 不可变指针 -> 可变指针
- 类型指针 -> 原始指针
Raw可以直接兼容没有Raw的类型指针。如:需要UnsafeRawPointer
类型参数时,可以直接传递UnsafePointer<Type>
。
隐式桥接
类型变量/常量、数组、字符串,在传递给指针参数时会进行隐式桥接。
Swift基本类型 -> 指针
即用指针访问Swift变量/常量。
使用withUnsafexxxPointer
函数,在闭包中用指针临时访问指向的变量/常量。
注意:在闭包中得到的指针不要返回出去。因为这是随外部变量/常量的生命周期影响,会因为其变量/常量销毁而变成野指针。
顶级函数:
1 | // 指向具体类型变量的类型指针 |
数组中的应用
withUnsafeBytes
、withUnsafeMutableBytes
withUnsafeBufferPointer
、withUnsafeMutableBufferPointer
1 | // withUnsafeMutableBytes应用 |
字符串中的应用
withCString
withUTF8
Data中的应用
withUnsafeBytes
withUnsafeMutableBytes
指针类型转换
注意Swift中的指针类型不存在继承关系。
类型指针 -> 原始指针
直接用原始指针的构造方法进行转换,如果是作为入参,则无需转换直接传递即可。
原始指针 -> 类型指针
永久绑定:bindMemory
、assumingMemoryBound
,两者操作的类型必须一致。
这种方式需要从原始指针调用,如果是类型指针要转换类型,则须转换为原始指针,再调用绑定类型方法。bindMemory
会导致原来的类型指针是未定义的,如果修改后兼容原来。
临时转换访问:withMemoryRebound
,该方法的访问发生在闭包参数中。
访问指向的值
若是类型指针(UnsafePointer<Type>
),可直接访问pointee
,可读写。
若是原始指针(UnsafeRawPointer
),使用load
方法,返回指定偏移的具体类型。只读。
当然对于可变原始指针(UnsafeMutableRawPointer
),有对应的写入方法:
copyMemory
:从其他原始指针拷贝字节数据storeBytes
:写入具体类型
C指针使用注意
在 Core Foundation 里,几乎所有用 Create 和 Copy 开头的函数,只要它们返回一个非托管的对象,我们几乎总是应该使用 takeRetainedValue() 方法来读取。