用Swift表示C函数中的NULL函数指针

10
考虑使用私有但有点记录的Cocoa C函数 _NSLogCStringFunction()_NSSetLogCStringFunction()_NSLogCStringFunction() 返回一个函数指针,指向Objective-C运行时在幕后使用的C函数,用于NSLog(),而_NSSetLogCStringFunction() 允许开发人员指定自己的C函数进行日志记录。 有关这两个函数的更多信息可以在此Stack Overflow问题此WebObjects支持文章中找到。
在C语言中,我可以将一个空的函数指针传递给_NSSetLogCStringFunction():
extern void _NSSetLogCStringFunction(void(*)(const char*, unsigned, BOOL));

_NSSetLogCStringFunction(NULL); // valid

然而,当我尝试在纯Swift中完成此操作时,遇到了一些问题:

/// Represents the C function signature used under-the-hood by NSLog
typealias NSLogCStringFunc = (UnsafePointer<Int8>, UInt32, Bool) -> Void

/// Sets the C function used by NSLog
@_silgen_name("_NSSetLogCStringFunction")
func _NSSetLogCStringFunction(_: NSLogCStringFunc) -> Void

_NSSetLogCStringFunction(nil) // Error: nil is not compatible with expected argument type 'NSLogCStringFunc' (aka '(UnsafePointer<Int8>, UInt32, Bool) -> ()')

如果我尝试使用unsafeBitCast来规避这个编译时警告,我的程序会崩溃,并显示EXC_BAD_INSTRUCTION(正如预期的那样,因为签名不正确):

let nullPtr: UnsafePointer<Void> = nil
let nullFuncPtr = unsafeBitCast(nullPtr, NSLogCStringFunc.self)
_NSSetLogCStringFunction(nullFuncPtr) // crash

我如何在Swift中表示指向(void*), (void(*)(const char*, unsigned, BOOL)) 或者 (UnsafePointer<Int8>, UInt32, Bool) -> VoidNULL函数指针?

3
无论什么原因,lol,立即被踩 - 连问题都没有时间读完。 - luk2302
1
@luk2302 我想我有个粉丝 :) 对我来说,那是一个新记录,44秒内-1。 - JAL
1个回答

5
Swift对(Objective-C)声明的映射
extern void _NSSetLogCStringFunction(void(*)(const char*, unsigned, BOOL));

public func _NSSetLogCStringFunction(_: (@convention(c) (UnsafePointer<Int8>, UInt32, ObjCBool) -> Void)!)

最简单的解决方案是将 Objective-C 的 extern 声明放入 Objective-C 头文件中,并从桥接头文件中包含它。

或者,在纯 Swift 中,可以这样实现:

typealias NSLogCStringFunc = @convention(c) (UnsafePointer<Int8>, UInt32, ObjCBool) -> Void

@_silgen_name("_NSSetLogCStringFunction")
func _NSSetLogCStringFunction(_: NSLogCStringFunc!) -> Void

在这两种情况下,函数参数是一个隐式解包的可选类型,你可以使用nil来调用它。例如:
func myLogger(message: UnsafePointer<Int8>, _ length: UInt32, _ withSysLogBanner: ObjCBool) -> Void {
    print(String(format:"myLogger: %s", message))
}

_NSSetLogCStringFunction(myLogger) // Set NSLog hook.
NSLog("foo")
_NSSetLogCStringFunction(nil) // Reset to default.
NSLog("bar")

输出:

myLogger: foo
2016-04-28 18:24:05.492 prog[29953:444704] bar
(注:此为原文内容,已无需翻译)

真的吗?我只需要将 Bool 改为 ObjCBool 吗?-_- Swift 中有太多的 Bool 类型了。谢谢 Martin。 - JAL
@JAL:还有 @convention(c) 和隐式展开参数。 - Martin R
实际上,我认为不需要使用ObjCBool。看起来我只是忘记了@convention(C)和隐式解包参数。使用Bool与其他两个一起似乎可以工作。你看到的也是这样吗? - JAL
@JAL:也许是这样,但是“生成的接口”显示的是Objective-C声明中的ObjCBool,所以我会使用它。 - Martin R
是的,我也看到了同样的事情。但是ObjCBool是否可以像CBool一样与Swift的Bool类型互换使用呢?还是这是一个特殊情况,因为CBoolBool的别名。 - JAL
2
@JAL:我在这里写了一些关于不同布尔值的内容:https://dev59.com/5lwX5IYBdhLWcg3wjwFZ#33667761。`BOOL`通常被映射为`Bool`,但并非总是如此。我不确定这是否适用于函数指针。但根据我的经验,“生成的接口”会显示正确的映射。 - Martin R

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接