一个安全、简单、高效的 Swift/Objective-C hook库,可以动态修改某个对象的方法或者一个类所有对象的某个方法。 它支持 Swift 和 Objective-C,与 KVO 具有良好的兼容性。
它基于 Objective-C 运行时和 libffi。
- hook某个实例的某个方法,在目标方法执行之前调用 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
- hook某个实例的某个方法,在目标方法执行之后调用 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
- hook某个实例的某个方法,用 hook 闭包完全取代目标方法。
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)")
- hook某个类的所有实例的某个方法,在目标方法执行之前调用 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
- hook某个类的某个类方法或静态方法,在目标方法执行之前调用 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()
使用 cocoapods.
pod 'EasySwiftHook'
或者使用 Swift Package Manager。 3.2.0 版本之后,SPM被支持。确保Xcode的版本大于等于12.5,否则会有编译错误。
和 Aspects 比较 (向 Aspects 致敬).
- Hook with Before and After mode for all instances, SwiftHook is 13 - 17 times faster than Aspects.
- Hook with Instead mode for all instances, SwiftHook is 3 - 5 times faster than Aspects.
- Hook with Before and After mode for specified instances, SwiftHook is 4 - 5 times faster than Aspects.
- Hook with Instead mode for specified instances, SwiftHook is 2 - 4 times faster than Aspects.
自 3.0.0 版本以来,完美兼容KVO。 更多测试用例: Here
We already have great Aspects. Why do I created SwiftHook?
- Aspects has some bugs. Click here for test code.
- Aspects doesn’t support Swift with instead mode in some cases. Click here for test code.
- Aspects’s API is not friendly for Swift.
- Aspects doesn’t support Swift object which is not based on NSObject.
- Aspects is based on message forward. This performance is not good.
- Aspects are no longer maintained. Author said: “STRICTLY DO NOT RECOMMEND TO USE Aspects IN PRODUCTION CODE”
- Aspects is not compatible with KVO.
BTW, Respect to Aspects!
- iOS 12.0+, MacOS 10.13+ (Unverified for tvOS, watchOS)
- Xcode 15.1+
- Swift 5.0+