私有API、Objective-C运行时与Swift | 作者:Victor Pavlychko

作者:API传播员 · 2025-11-14 · 阅读时间:4分钟

有时在开发应用程序时,我们可能会遇到需要使用私有 API 的情况。这些 API 可能提供了一些尚未公开的重要功能,或者可以用来解决已知的平台问题。也有可能,我们只是为了调试代码,想要获取更多的细节信息。

Swift 为这种场景增加了一个新的维度:标记为 NS_Swift_UNAVAILABLE 的受限 API。从技术上讲,这些 API 并非真正的私有,但由于安全性不足,它们不会在 Swift 中直接暴露。然而,调用这些不可用的 API 是可行的,因为它们通常有完整的文档,并且可以通过 Objective-C 代码访问。本文将重点介绍如何访问一组 Swift 不可用的 API,这些 API 涉及 NSMethodSignatureNSInvocation 以及与动态消息调度相关的类。如果你对这些内容不熟悉,建议先查阅相关文档。


Objective-C 运行时与私有 API

在涉及到 Objective-C 运行时时,几乎没有什么是完全私有的。尽管直接方法的引入稍微改变了这一点,但只要你知道如何调用这些 API,它们仍然是可访问的。

在 Objective-C 时代,调用未公开的 SDK 方法相对简单。由于 Objective-C 的动态特性,可以通过为私有方法声明一个类别来实现。这些方法会在运行时自动解析。然而,Swift 是一种类型安全的语言,这种方式在 Swift 中已经不可行。

在处理复杂签名时,常见的做法是依赖 Swift 兼容的 Foundation API,例如简单函数或低级的 method_getImplementation 和/或 objc_msgSend。但这种方式通常会导致代码复杂、冗长且容易出错。


更清洁的解决方案

我们可以借鉴 Objective-C 中的分类技巧,定义一个包含目标方法的 @objc 协议,通过运行时为目标类动态添加一致性,然后利用 Swift 的类型转换功能。由于 Objective-C 的动态调度特性,该协议会自动实现我们需要的现有类方法。

定义协议

首先,为 NSMethodSignature 定义一个协议,该协议需要与 Objective-C 文档中的内容相匹配。这里需要特别注意 @objc(getArgumentTypeAtIndex:) 注解。通常,编译器会根据方法名称生成适当的选择器,但在某些情况下,我们可能需要手动更改生成的名称。正确的选择器名称至关重要,因为它必须与底层 API 签名完全匹配。

@objc protocol NSMethodSignaturePrivate {
    @objc(getArgumentTypeAtIndex:)
    func getArgumentType(at index: UInt) -> UnsafePointer
}

动态附加协议

定义协议后,可以通过显式的 Objective-C 运行时调用将其附加到目标类:

let methodSignature = NSClassFromString("NSMethodSignature") as! NSObject.Type
let selector = NSSelectorFromString("signatureWithObjCTypes:")
let method = class_getClassMethod(methodSignature, selector)

虽然这段代码还缺少错误检查,但它是一个很好的辅助函数候选。测试一切正常后,可以进一步完善。


减少样板代码

在实现类导入代码时,可以尝试通过泛型减少协议引用的重复。例如:

func createSignature(for type: T.Type) -> T? {
    // 示例代码
}

然而,这种方法可能会遇到编译错误,例如:

静态成员 signature(objCTypes:) 不能在协议元类型 NSMethodSignaturePrivate.Protocol 上使用。

这是因为 NSMethodSignaturePrivate 是一个协议,而不是具体的符合类型。为了调用类函数 signature(objCTypes:),需要一个具体的类型。

使用 Swift 反射 API

我们可以通过 Swift 反射 API 获取协议名称,然后利用 objc_getProtocol 找到对应的运行时协议:

let protocolName = "NSMethodSignaturePrivate"
if let protocolObj = objc_getProtocol(protocolName) {
    // 使用 protocolObj
}

总结

Objective-C 运行时结合 Swift 的强大表现力,为开发者提供了许多可能性。通过一些技巧,我们可以访问 Swift 限制的或私有的 API,就像在 Objective-C 中那样。不过,需要注意的是,这种方式可能会绕过一些安全机制,因此需要谨慎使用。

完整的示例代码可以参考以下链接:
https://gist.github.com/victor-pavlychko/8a896917d8c73f4dded594ab4782214e

原文链接: https://medium.com/@victor.pavlychko/private-apis-objective-c-runtime-and-swift-ceaeefbb6e48