Swift函数类型修饰符@convention(c)
Swift函数类型修饰符@convention(c)

Swift函数类型修饰符@convention(c)

在 Swift 中,@convention(c) 是一个函数类型修饰符,表示该函数遵循 C 语言的调用约定(Calling Convention),也就是编译器安排参数、返回值、栈帧的方式必须与 C 兼容。

它通常用于:

1、与 C 语言或 C 库交互时。

2、将 Swift 的函数/闭包转换成可以传递给 C 函数的函数指针。

3、使用系统 API(如 macOS 的 AXObserverCreate、CFRunLoopSourceCreate 等)时,要求提供 @convention(c) 函数指针。

使用示例

public func AXObserverCreate(_ application: pid_t, _ callback: AXObserverCallback, _ outObserver: UnsafeMutablePointer<AXObserver?>) -> AXError
public typealias AXObserverCallback = @convention(c) (AXObserver, AXUIElement, CFString, UnsafeMutableRawPointer?) -> Void

这段代码定义了一个函数类型AXObserverCreate,它的callback参数是一个符合 C 调用约定的函数指针:

@convention(c) (AXObserver, AXUIElement, CFString, UnsafeMutableRawPointer?)

在Swift中,只有以下两种类型符合C 调用约定的函数指针:

1、全局函数;

2.、符合C调用约定的闭包;

类型方法(static func、class func)无法作为C函数指针使用。

1)错误用法,如果我们在Class中定义方法

func callback(observer: AXObserver, element: AXUIElement, notification: CFString, context: UnsafeMutableRawPointer?) {
    print("Received AX notification: \(notification)")
}

函数传给C函数时,就会报错:

A C function pointer can only be formed from a reference to a 'func' or a literal closure

报错的原因在于,C 函数指针必须是全局地址的函数指针,而 Swift 中的实例方法其实是带有隐藏参数(self)的动态方法,不能直接转换为 @convention(c) 的函数指针。

func callback(...) { } // 实际上是 (self: AXMonitor) -> (...) -> Void

在调用时,Swift 自动把 self 传给它,这跟 C 的调用方式不一致(C 函数指针不接受 self)。

关键点:实例方法不能符合 C 的函数指针要求,因为它不是纯粹的函数地址。

2)如果改为属性的话

let callback: AXObserverCallback = { observer, element, notification, refcon in
    print("接收到辅助功能通知")
}

就可以把这个函数指针传给 C 函数,比如:

AXObserverCreate(pid, Self.callback, &observer)

这是因为这个闭包其实是Swift 全局函数闭包的等价写法,Swift 会将其自动桥接为符合 @convention(c) 的函数指针,相当于手动声明了一个:

typealias AXObserverCallback = @convention(c) (AXObserver, AXUIElement, CFString, UnsafeMutableRawPointer?) -> Void

所以,这个闭包可以被编译器桥接为 C 调用约定的函数指针。

使用场景

1、AXObserverCreate;

2、CFRunLoopSourceCreate;

3、qsort(标准 C 排序函数);

4、Core Foundation 中要求 C 函数指针的 API;

5、注册回调函数到 C 语言库。

系统定义:很多系统 API,比如 AXObserverCallback、CFRunLoopPerformCallBack、CGEventTapCallBack 等等,都要求用 @convention(c) 修饰的函数指针,这是因为它们底层是用 C 或 Core Foundation 写的,使用 C 调用约定(Calling Convention)。

自己定义:也可以自己声明函数类型使用 @convention(c),例如:

typealias MyCFunc = @convention(c) (Int32) -> Void

func callCFunc(_ fn: MyCFunc) {
    fn(42)
}

注意:@convention(c)只能用于闭包函数定义或者函数类型的类型注释:

// 使用 @convention(c) 正确定义函数类型
static let callback: @convention(c) AXObserverCallback = { observer, element, notification, context in
    print("收到 AX 通知: \(notification)")
}

不可以用在Swift的方法声明上:

@convention(c)  // 报错:Attribute can only be applied to types, not declarations
static func callback(observer: AXObserver, element: AXUIElement, notification: CFString, context: UnsafeMutableRawPointer?) {
    print("Received AX notification: \(notification)")
}

注意事项

@convention(c) 闭包不能捕获上下文(即不能访问外部变量,闭包不能包含Self)。

参数和返回值类型必须是 C 可以识别的类型,如指针、整数、Void 等。

总结

@convention(c) 是一个函数类型修饰符,表示该函数遵循 C 语言的调用约定(Calling Convention),通常用于与C 语言相关内容交互时使用。

   

如果您认为这篇文章给您带来了帮助,您可以在此通过支付宝或者微信打赏网站开发者。

欢迎加入我们的 微信交流群QQ交流群,交流更多精彩内容!
微信交流群二维码 QQ交流群二维码

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注