一个安全、简单且高效的 Swift/Objective-C Hook 库,可动态修改特定对象或类中所有对象的方法。 它同时支持 Swift 和 Objective-C,并且与 Key-Value Observing (KVO) 具有出色的兼容性。
它基于 Objective-C 运行时和 libffi。
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
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
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)")
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
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 集成 SwiftHook。
pod 'EasySwiftHook'
或者使用 Swift Package Manager。 从 3.2.0 开始支持 SPM。 确保您的 Xcode 大于 12.5,否则会编译错误。
与 Aspects 比较(尊重 Aspects)。
从 3.0.0 版本开始,SwiftHook 完全兼容 KVO。 更多测试用例:这里
顺便说一句,向 Aspects 致敬!