中文

什么是 SwiftHook?

一个安全、简单且高效的 Swift/Objective-C Hook 库,可动态修改特定对象或类中所有对象的方法。 它同时支持 Swift 和 Objective-C,并且与 Key-Value Observing (KVO) 具有出色的兼容性。

它基于 Objective-C 运行时和 libffi

如何使用 SwiftHook

  1. 在执行指定实例的方法之前调用 hook 闭包。
class TestObject { // No need to inherit from NSObject.
    // Use `@objc` to make this method accessible from Objective-C
    // Using `dynamic` tells Swift to always refer to Objective-C dynamic dispatch
    @objc dynamic func testMethod() {
        print("Executing `testMethod`")
    }
}

let obj = TestObject()

let token = try hookBefore(object: obj, selector: #selector(TestObject.testMethod)) {
    print("Before executing `testMethod`")
}

obj.testMethod()
token.cancelHook() // cancel the hook
  1. 在执行指定实例的方法之后调用 hook 闭包,并获取参数。
class TestObject {
    @objc dynamic func testMethod(_ parameter: String) {
        print("Executing `testMethod` with parameter: \(parameter)")
    }
}

let obj = TestObject()

let token = try hookAfter(object: obj, selector: #selector(TestObject.testMethod(_:)), closure: { obj, sel, parameter in
    print("After executing `testMethod` with parameter: \(parameter)")
} as @convention(block) ( // Using `@convention(block)` to declares a Swift closure as an Objective-C block
    AnyObject, // `obj` Instance
    Selector, // `testMethod` Selector
    String // first parameter
) -> Void // return value
)

obj.testMethod("ABC")
token.cancelHook() // cancel the hook
  1. 完全覆盖指定实例的方法。
class Math {
    @objc dynamic func double(_ number: Int) -> Int {
        let result = number * 2
        print("Executing `double` with \(number), result is \(result)")
        return result
    }
}

let math = Math()

try hookInstead(object: math, selector: #selector(Math.double(_:)), closure: { original, obj, sel, number in
    print("Before executing `double`")
    let originalResult = original(obj, sel, number)
    print("After executing `double`, got result \(originalResult)")
    print("Triple the number!")
    return number * 3
} as @convention(block) (
    (AnyObject, Selector, Int) -> Int,  // original method block
    AnyObject, // `math` Instance
    Selector, // `sum` Selector
    Int // number
) -> Int // return value
)

let number = 3
let result = math.double(number)
print("Double \(number), got \(result)")
  1. 在执行类的所有实例的方法之前调用 hook 闭包。
class TestObject {
    @objc dynamic func testMethod() {
        print("Executing `testMethod`")
    }
}

let token = try hookBefore(targetClass: TestObject.self, selector: #selector(TestObject.testMethod)) {
    print("Before executing `testMethod`")
}

let obj = TestObject()
obj.testMethod()
token.cancelHook() // cancel the hook
  1. 在执行类方法之前调用 hook 闭包。
class TestObject {
    @objc dynamic class func testClassMethod() {
        print("Executing `testClassMethod`")
    }
    @objc dynamic static func testStaticMethod() {
        print("Executing `testStaticMethod`")
    }
}

try hookClassMethodBefore(targetClass: TestObject.self, selector: #selector(TestObject.testClassMethod)) {
    print("Before executing `testClassMethod`")
}
TestObject.testClassMethod()

try hookClassMethodBefore(targetClass: TestObject.self, selector: #selector(TestObject.testStaticMethod)) {
    print("Before executing `testStaticMethod`")
}
TestObject.testStaticMethod()
  1. Hooking dealloc 方法
  2. 仅 Hook 一次 (触发后取消 Hook)
  3. 在 Objective-C 中使用

如何集成 SwiftHook?

可以通过 cocoapods 集成 SwiftHook

pod 'EasySwiftHook'

或者使用 Swift Package Manager。 从 3.2.0 开始支持 SPM。 确保您的 Xcode 大于 12.5,否则会编译错误。

性能

Aspects 比较(尊重 Aspects)。

与 KVO 的兼容性

从 3.0.0 版本开始,SwiftHook 完全兼容 KVO。 更多测试用例:这里

我们已经有了很棒的 Aspects。 我为什么要创建 SwiftHook?

  1. Aspects 存在一些错误。点击这里查看测试代码
  2. 在某些情况下,Aspects 不支持 Swift 的 Instead 模式。点击这里查看测试代码
  3. Aspects 的 API 对 Swift 不友好。
  4. Aspects 不支持不是基于 NSObject 的 Swift 对象。
  5. Aspects 基于消息转发。 这种性能不好。
  6. Aspects 不再维护。 作者说:“强烈建议不要在生产代码中使用 Aspects
  7. Aspects 与 KVO 不兼容。

顺便说一句,向 Aspects 致敬!

要求