EasySwiftHook 3.5.2

EasySwiftHook 3.5.2

YanniWang 维护。



  • 作者
  • 王氩(Yanni Wang)

这是什么?

一个安全、简单、强大且高效的 iOS Hook 库(支持 Swift 和 Objective-C,与 KVO 兼容良好)。

它基于 iOS 运行时和 libffi

如何安装

您可以使用 cocoapods 集成 SwiftHook

pod 'EasySwiftHook'

如何使用

  1. 在执行 指定实例 的方法之前调用 hook 闭包。
class MyObject { // The class doesn’t have to inherit from NSObject. of course inheriting from NSObject works fine.
    @objc dynamic func sayHello() { // The key words of methods `@objc` and `dynamic` are necessary.
        print("Hello!")
    }
}

do {
    let object = MyObject()
    // WARNING: the object will retain the closure. So make sure the closure doesn't retain the object to avoid memory leak by cycle retain. If you want to access the obeject, please refer to 2nd guide below "XXX and get the parameters.".
    let token = try hookBefore(object: object, selector: #selector(MyObject.sayHello)) {
        print("You will say hello, right?")
    }
    object.sayHello()
    token.cancelHook() // cancel the hook
} catch {
    XCTFail()
}
  1. 在执行 指定实例 的方法之后调用 hook 闭包。并获得参数。
class MyObject {
    @objc dynamic func sayHi(name: String) {
        print("Hi! \(name)")
    }
}

do {
    let object = MyObject()
    
    // 1. The first parameter mush be AnyObject or NSObject or YOUR CLASS (In this case. It has to inherits from NSObject, otherwise will build error with "XXX is not representable in Objective-C, so it cannot be used with '@convention(block)'").
    // 2. The second parameter mush be Selector.
    // 3. The rest of the parameters are the same as the method.
    // 4. The return type mush be Void if you hook with `before` and `after` mode.
    // 5. The key word `@convention(block)` is necessary
    let hookClosure = { object, selector, name in
        print("Nice to see you \(name)")
        print("The object is: \(object)")
        print("The selector is: \(selector)")
    } as @convention(block) (AnyObject, Selector, String) -> Void
    let token = try hookAfter(object: object, selector: #selector(MyObject.sayHi), closure: hookClosure)
    
    object.sayHi(name: "Yanni")
    token.cancelHook()
} catch {
    XCTFail()
}
  1. 完全覆盖 指定实例 的方法。
class MyObject {
    @objc dynamic func sum(left: Int, right: Int) -> Int {
        return left + right
    }
}

do {
    let object = MyObject()
    
    // 1. The first parameter mush be an closure. This closure means original method. The parameters of it are the same as "How to use: Case 2". The return type of it must be the same as original method's.
    // 2. The rest of the parameters are the same as "How to use: Case 2".
    // 3. The return type mush be the same as original method's.
    let hookClosure = {original, object, selector, left, right in
        let result = original(object, selector, left, right)
        // You can call original with the different parameters:
        // let result = original(object, selector, 12, 27).
        // You also can change the object and selector if you want. Don't even call the original method if needed.
        print("\(left) + \(right) equals \(result)")
        return left * right
    } as @convention(block) ((AnyObject, Selector, Int, Int) -> Int, AnyObject, Selector, Int, Int) -> Int
    let token = try hookInstead(object: object, selector: #selector(MyObject.sum(left:right:)), closure: hookClosure)
    let left = 3
    let right = 4
    let result = object.sum(left: left, right: right)
    print("\(left) * \(right) equals \(result)")
    token.cancelHook()
} catch {
    XCTFail()
}
  1. 在执行 类中所有实例 的方法之前调用 hook 闭包。
class MyObject {
    @objc dynamic func sayHello() {
        print("Hello!")
    }
}

do {
    let token = try hookBefore(targetClass: MyObject.self, selector: #selector(MyObject.sayHello)) {
        print("You will say hello, right?")
    }
    MyObject().sayHello()
    token.cancelHook()
} catch {
    XCTFail()
}
  1. 在执行 类方法 之前调用 hook 闭包。
class MyObject {
    @objc dynamic class func sayHello() {
        print("Hello!")
    }
}

do {
    let token = try hookClassMethodBefore(targetClass: MyObject.self, selector: #selector(MyObject.sayHello)) {
        print("You will say hello, right?")
    }
    MyObject.sayHello()
    token.cancelHook()
} catch {
    XCTFail()
}
  1. 在 Objective-C 中使用

  2. Hook dealloc

性能

Aspects(相对于 Aspects)相比。

  • SwiftHook 对所有实例使用 Before 和 After 模式,比 Aspects 快 13 - 17 倍
  • 对所有实例使用 Instead 模式的 Hook,SwiftHook 比 Aspects 快 3 - 5 倍
  • 对指定实例使用 Before 和 After 模式的 Hook,SwiftHook 比阿斯谱快 4 - 5 倍
  • 对指定实例使用 Instead 模式的 Hook,SwiftHook 比阿斯谱快 2 - 4 倍

与 KVO 的兼容性

从 3.0.0 版本开始,SwiftHook 兼容 KVO。更多信息可在此处找到:链接

已经有了优秀的 Aspects,我为什么创建 SwiftHook?

  1. Aspects 存在一些错误。有关测试代码,请点击此处
  2. 在某些情况下,Aspects 不支持使用 Instead 模式的 Swift。有关测试代码,请点击此处
  3. Aspects 的 API 对 Swift 不友好。
  4. Aspects 不支持非基于 NSObject 的 Swift 对象。
  5. Aspects 基于 消息转发。这种性能不太好。
  6. Aspects 已不再维护。作者说:“严格禁止在生产代码中使用 Aspects
  7. Aspects 与 KVO 不兼容。

顺便说一句,尊重 Aspects!

它是如何工作的?

  1. 什么是 libffi
    1. 运行时调用 C 函数。
    2. 运行时创建闭包 (IMP)。
  2. SwiftHook 的逻辑.

要求

  • iOS 10.0+(未验证 macOS、tvOS、watchOS)
  • Xcode 11+
  • Swift 5.0+

关注者随时间的变化

Stargazers over time