Как подключать (swizzle) методы в Swift? - PullRequest
0 голосов
/ 30 мая 2020

Есть ли простой способ перехвата (swizzle) методов в Swift?

1 Ответ

0 голосов
/ 31 мая 2020

Эта структура может помочь: https://github.com/623637646/SwiftHook

Как использовать

Например, это ваш класс

class MyObject {
    @objc dynamic func noArgsNoReturnFunc() {
    }
    @objc dynamic func sumFunc(a: Int, b: Int) -> Int {
        return a + b
    }
    @objc dynamic class func classMethodNoArgsNoReturnFunc() {
    }
}

#f03c15 Ключевые слова методов @objc и dynamic необходимы

#f03c15 Класс не должен наследовать от NSObject. Если класс написан Objective- C, просто подключите его без дополнительных усилий

  1. Выполните закрытие хука перед выполнением указанного метода экземпляра.
let object = MyObject()
let token = try? hookBefore(object: object, selector: #selector(MyObject.noArgsNoReturnFunc)) {
    // run your code
    print("hooked!")
}
object.noArgsNoReturnFunc()
token?.cancelHook() // cancel the hook
Выполнить закрытие ловушки после выполнения метода указанного экземпляра. И получаем параметры.
let object = MyObject()
let token = try? hookAfter(object: object, selector: #selector(MyObject.sumFunc(a:b:)), closure: { a, b in
    // get the arguments of the function
    print("arg1 is \(a)") // arg1 is 3
    print("arg2 is \(b)") // arg2 is 4
    } as @convention(block) (Int, Int) -> Void)
_ = object.sumFunc(a: 3, b: 4)
token?.cancelHook() // cancel the hook

#f03c15 Необходимо ключевое слово @convention(block)

#f03c15 Для крючка на before и after. Аргументы закрытия должны быть пустыми или такими же, как и у метода. Тип возврата должен быть void

. Полностью переопределить метод для указанного экземпляра. Вы можете вызвать оригинал с одинаковыми или разными параметрами. Даже не вызывайте оригинальный метод, если хотите.
let object = MyObject()
let token = try? hookInstead(object: object, selector: #selector(MyObject.sumFunc(a:b:)), closure: { original, a, b in
    // get the arguments of the function
    print("arg1 is \(a)") // arg1 is 3
    print("arg2 is \(b)") // arg2 is 4

    // run original function
    let result = original(a, b) // Or change the parameters: let result = original(-1, -2)
    print("original result is \(result)") // result = 7
    return 9
    } as @convention(block) ((Int, Int) -> Int, Int, Int) -> Int)
let result = object.sumFunc(a: 3, b: 4) // result
print("hooked result is \(result)") // result = 9
token?.cancelHook() // cancel the hook

#f03c15 Для хука с instead. Первым аргументом замыкания должно быть замыкание, которое имеет те же типы, что и метод. Остальные аргументы и возвращаемый тип должны быть такими же, как и у метода.

Выполнить закрытие обработчика перед выполнением метода всех экземпляров класса.
let token = try? hookBefore(targetClass: MyObject.self, selector: #selector(MyObject.noArgsNoReturnFunc)) {
    // run your code
    print("hooked!")
}
MyObject().noArgsNoReturnFunc()
token?.cancelHook() // cancel the hook
Выполните закрытие обработчика перед выполнением метода класса.
let token = try? hookClassMethodBefore(targetClass: MyObject.self, selector: #selector(MyObject.classMethodNoArgsNoReturnFunc)) {
    // run your code
    print("hooked!")
}
MyObject.classMethodNoArgsNoReturnFunc()
token?.cancelHook() // cancel the hook
...