在使用Swift开发的时候,有时候会遇到与C语言交互,调用C语言的API。
// test.h
int add(int a, int b) ;
// test.c
int add(int a, int b) {
return a + b;
}
在.h文件中有一个add函数的声明,在.c文件中有add函数的实现,这种非常简单的C语言函数,在Swift中的调用也非常简单,直接使用即可
let x = add(20, 30)
print(x)
但是如果是C语言的指针该如何调用呢
// test.h
int callback(void (* execute)(void *), void * obj) ;
// test.c
int callback(void (* execute)(void *), void * obj) {
execute(obj);
return 0;
}
声明一个返回值为int
的函数,函数名称为callback
,里面接收2个参数。
第一个参数 void (* execute)(void *)
是一个名称为execute
的函数指针,它的返回值是void
,参数是一个void *
指针
第二个参数是void *
指针
这个函数的作用就是接收一个指针,并将这个指针作为参数传递给函数指针。
那么如何调用上面这个C语言函数呢?
直接看代码吧
let user = User()
let userPtr = Unmanaged<User>.passRetained(user).toOpaque()
class User {
func eat() {
print(#file, #line, "eat now")
}
deinit {
print("User instance deinit")
}
}
首先定义一个User
类,生成实例对象,接下来操控的都是user
实例的指针,如果想取得的它的指针,就需要使用Unmanaged
,如上,就取到了userPtr
指针
var closureTest: @convention(c) (UnsafeMutableRawPointer?) -> Void = { userPtr in
guard let userPtr = userPtr else { return }
let user = Unmanaged<User>.fromOpaque(userPtr).takeRetainedValue()
user.eat()
}
然后,定义一个符合void (* execute)(void *)
类型的闭包closureTest
,在swift符合类型的闭包写法就是这样的,必须要加上@convention(c)
表明这是一个针对C语言的类型,UnsafeMutableRawPointer?
就是Swift中指针的表示方式,使用之所以加问号,使用可选类型,是因为在C语言中它可以是空指针,可以为nil。使用Unmanaged
将指针还原为user
实例对象,调用eat
方法,证明这个还原成功。
不要忘记在桥接文件中导入C语言的.h文件
callback(closureTest, userPtr)
这样就可以使用这个C语言函数了,他的原理很简单,就是取出实例对象的指针,再传递指针。
这里有一个很重要的类型就是Unmanaged,他是一个结构体,主要包含以下方法。
public struct Unmanaged<Instance> where Instance : AnyObject {
public struct Unmanaged<Instance> where Instance : AnyObject {
// 将实例对象作为属性,之后操作的其实都是这个属性
internal unowned(unsafe) var _value: Instance
// 从实例对象初始化一个Unmanaged对象,说白了就是对实例对象的一层封装
internal init(_private: Instance) { _value = _private }
// 从一个指针中还原Unmanaged对象
public static func fromOpaque(_ value: UnsafeRawPointer) -> Unmanaged<Instance>
// 将一个Unmanaged对象转换为指针
public func toOpaque() -> UnsafeMutableRawPointer
// 将实例对象包装为Unmanaged对象,并且引用计数 +1
public static func passRetained(_ value: Instance) -> Unmanaged<Instance>
// 将实例对象包装为Unmanaged对象
public static func passUnretained(_ value: Instance) -> Unmanaged<Instance>
// 从Unmanaged对象中还原实例对象
public func takeUnretainedValue() -> Instance
// 从Unmanaged对象中还原实例对象,并且引用计数 -1
public func takeRetainedValue() -> Instance
// 引用计数 +1
public func retain() -> Unmanaged<Instance>
// 引用计数 -1
public func release()
// 自动释放
public func autorelease() -> Unmanaged<Instance>
}
它是如何实现的呢?
查看Swift开源代码可以帮助我们 Unmanaged.swift
还是很容易看懂的,Unmanaged
其实就是实例对象和指针之间的媒介,负责相互之间的转换,需要注意的是平时使用的时候passRetained
和takeRetainedValue
是一对,passUnretained
和takeUnretainedValue
是一对。
如果熟悉引用计数的话就明白,前者在使用的时候进行了引用计数的增减,后者并没有,如果传递的对象中途有被销毁的风险,就要用前者。引用计数,有增就有减。
后记
public func retain() -> Unmanaged {
Builtin.retain(_value)
return self
}
在源码中看到Builtin.retain(_value)
,特别感兴趣想去查看Builtin
是什么,他是如何计算引用计数的,retain
release
的如何实现的?
但是在Swift开源代码里没有找到,网上的信息也不是很多,在论坛看了几篇其他人的提问,结论就是Builtin
就是LLVM
编译器内置代码与外界的map,例如+
符号的定义,Int
类型的定义就是需要Bulitin
,具体在里面做了什么,就是我们不得而知了的,他是闭源的。
Swift Forums上面的提问
Swift Forums
一篇很古老2016年的文章
swift-mysterious-builtin-module
网友评论