InterposeKit 0.0.1

InterposeKit 0.0.1

Peter Steinberger 维护。



  • Peter Steinberger

InterposeKit

SwiftPM xcodebuild pod lib lint Xcode 11.4+ Swift 5.2+

InterposeKit 是一个使用 Swift 编写的现代库,用于优雅地 swizzle。它是完全使用 Swift 5.2+ 编写的,并且可以在 @objc dynamic Swift 函数或 Objective-C 实例方法上工作。API 文档在interposekit.com 可用,并且在我的博客上的一些实现想法

不同于基于method_exchangeImplementations(见method_exchangeImplementations)添加新方法和交换实现,这个库直接使用class_replaceMethod(见class_replaceMethod)来替换实现。这避免了swizzling的常见问题

您可以在方法调用之前、之后或替代原有实现添加代码。
这与Aspects库类似,但目前还不支持动态子类。

使用方法

假设你要修改TestClass中的sayHi

class TestClass: NSObject {
    @objc dynamic func sayHi() -> String {
        print("Calling sayHi")
        return "Hi there 👋"
    }
}

try Interpose(TestClass.self) {
    try $0.hook(#selector(TestClass.sayHi), { store in { `self` in
        print("Before Interposing \(`self`)")

        let string = store((@convention(c) (AnyObject, Selector) -> String).self)(`self`, store.selector)

        print("After Interposing \(`self`)")
        return string + testSwizzleAddition
        }
        as @convention(block) (AnyObject) -> String})
}

调用print(TestClass().sayHi())时,我们会得到以下结果。

[Interposer] Swizzled -[TestClass.sayHi] IMP: 0x000000010d9f4430 -> 0x000000010db36020
Before Interposing <InterposeTests.TestClass: 0x7fa0b160c1e0>
Calling sayHi
After Interposing <InterposeTests.TestClass: 0x7fa0b160c1e0>
Hi there 👋 and Interpose

关键事实

  • 直接修改 方法 的实现,比基于选择器的 swizzling 更好。
  • 纯 Swift,没有使用 NSInvocation,这需要装箱并且可能会比较慢。
  • 没有类型检查。如果你有拼写错误或忘记 约定 部分,这将在运行时崩溃。
  • 是的,你需要两次输入结果类型。这是一个权衡,否则我们需要使用 NSInvocation 或汇编。
  • 延迟插桩有助于在运行时加载类的场景。这对于 Mac Catalyst 很有用。

延迟挂钩

有时可能需要挂钩系统框架中的一个位于深处的类,该类是在晚些时候加载的。Interpose有针对此问题的解决方案,并在动态链接器中使用一个挂钩来通知何时加载新类。

try Interpose.whenAvailable(["RTIInput", "SystemSession"]) {
    let lock = DispatchQueue(label: "com.steipete.document-state-hack")
    try $0.hook("documentState", { store in { `self` in
        lock.sync {
            store((@convention(c) (AnyObject, Selector) -> AnyObject).self)(`self`, store.selector)
        }} as @convention(block) (AnyObject) -> AnyObject})

    try $0.hook("setDocumentState:", { store in { `self`, newValue in
        lock.sync {
            store((@convention(c) (AnyObject, Selector, AnyObject) -> Void).self)(`self`, store.selector, newValue)
        }} as @convention(block) (AnyObject, AnyObject) -> Void})
}

常见问题

为什么不命名为跨接呢?“套件”感觉太老套了。

原计划将其命名为跨接,但随后出现了SR-898问题。虽然模块名称相同的类在大多数情况下是可以工作的,但当启用分发构建时,这会导致问题有关修复此问题的讨论已有一些了,但这将在2020年末实现,如果真的能实现的话。

我想钩入 Swift!你又一如既往地做了 ObjC swizzle 操作,为什么?

UIKit 和 AppKit 还没有消失,问题也还没有消失。我认为这是一个不常需要的工具来解决系统级问题。Swift 中有一些方法可以解决这些问题,但那是一个独立(且更困难!)的项目。

我可以发这个吗?

当然可以。这个项目的目标是一个不力求显示过聪明才智的简单库。我在Aspects中这样做过,虽然我非常喜欢它,但它存在一些问题,可能会与其他试图显示聪明才智的代码产生副作用。InterposeKit很普通,所以你不必担心像“我们给我们的应用程序添加了New Relic,现在你的东西崩溃了”这样的问题。

已经2020年了,就连安全漏洞现在都有标识了。与时俱进吧!甚至安全漏洞现在都有标识了

我知道,我明白。很快就会提升的!

它不执行 X

欢迎提交拉取请求!在提交之前最好先打开一个草稿来规划你的计划,我希望保持功能集最小化,使其保持简单无魔法。

安装

构建 InterposeKit 需要 Xcode 11.4 或更高版本或 Swift 5.2 或更高版本的工具链,并带有 Swift Package Manager。

Swift包管理器

将以下代码.package(url: "https://github.com/steipete/InterposeKit.git", from: "0.0.1")添加到您的Package.swift文件中的dependencies部分。

Cocoapods

pod 'InterposeKit' 添加到您的 Podfile 中。

Carthage

github "steipete/InterposeKit" 添加到您的 Cartfile 文件中。

改进建议

  • 编写提案,允许将现有类型的调用约定转换为其他约定
  • 使用C块结构在方法类型和C类型之间进行类型检查(我在Aspects库中这样做),这仍然会导致运行时崩溃,但可能是在钩子时间而不是调用它时发生。
  • 添加基于对象的钩子,使用动态子类化(再次使用Aspects)。
  • 为纯C函数添加dyld_dynamic_interpose钩子。
  • 将Promise-API用于Interpose.whenAvailable,以实现更好的错误冒泡。
  • 尝试使用Swift钩子?⚡️
  • 针对Swift Nightly进行测试,作为Chron Jpb。
  • 我相信还有更多——欢迎拉取请求或评论

实现这一目标: Carthage兼容 CocoaPods

感谢

特别感谢JP Simard,他在使用GitHub Actions搭建Yams方面做出了巨大的贡献——这极大地方便了在这里快速构建CI。

许可证

InterposeKit遵循MIT许可证。