Методы, имена и типы параметров


#1

Приветствую! Пытаюсь разобраться с Objective-C Runtime.

Есть класс, для которого через extension определены определенные методы, список из которых мне хотелось бы получить, включая наименования и типы параметров. Что-то похожее я могу получить через print(#function, type(of: method)) - но это если вызывать внутри этого method, а необходимо извне.

Пробую class_copyMethodList и method_getName, но получаются какие-то кривые наименования, например

method3WithArg1:arg2:

, тогда как через #function, type(of: method) получаю

method3(arg1:arg2:) (Int, Double) -> ()

method_getNumberOfArguments выдает кол-во параметров на 2 больше, а через method_getArgumentType получаются какие-то типы q, d, @ )) Последние 2 еще ладно, но почему q вместо f - не понятно (https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Strings/Articles/formatSpecifiers.html). Лучше бы что-нибудь привычное вроде

method3(arg1: Int, arg2: Double)

Вопрос: это только вручную можно исправить? Или может есть какие-то другие подходы?

Далее хотелось бы из этого списка выбрать, например, второй метод, выяснить какие ему требуются параметры, определить его в chosenMethod, чтобы потом уже можно было его вызвать. Как можно из Selector сделать closure, и вообще нужны ли селекторы?

В общем и целом, есть запрограммированные функции, которые будут постепенно добавляться. Пользователю предлагается выбрать какую-либо из существующих, запросить у него параметры для этой функции и просто вызвать ее.

class MyClass {
    
    var chosenMethod: ((MyClass) -> ())?
    
    func execute() {
        chosenMethod?(self)
    }
}

@objc extension MyClass {
    
    func method0() {
        print(#function, type(of: method0), "\n")
    }
    
    func method1(_ arg1: String) {
        print(#function, type(of: method1), "\n")
    }
    
    func method2(_ arg1: Int, _ arg2: Double) {
        print(#function, type(of: method2), "\n")
    }
    
    func method3(arg1: Int, arg2: Double) {
        print(#function, type(of: method3), "\n")
    }
}

func getMethods() -> [Selector] {
    
    var methodCount: UInt32 = 0
    guard let methodList = class_copyMethodList(MyClass.self, &methodCount) else { return [] }
    
    var selectors = [Selector]()
    let maxChars = 256
    let ctype = UnsafeMutablePointer<Int8>.allocate(capacity: maxChars)
    
    for i in 0 ..< Int(methodCount) {
        
        let method = methodList[i]
        let selector: Selector = method_getName(method)
        selectors.append(selector)
        
        print("name: \(String(cString: sel_getName(selector)))")
        
        let numberOfArguments = method_getNumberOfArguments(method) - 2
        print("numberOfArguments: \(numberOfArguments)")
        
        for j in 2 ..< Int(numberOfArguments) + 2 {
            method_getArgumentType(method, UInt32(j), ctype, maxChars)
            print("argumentType: \(String(cString: ctype))")
        }
        
        print()
    }
    
    free(methodList)
    
    return selectors
}

//

let myClass = MyClass()

myClass.method0()
myClass.method1("check")
myClass.method2(0, 1.0)
myClass.method3(arg1: 0, arg2: 1.0)

print("---\n")

let methods = getMethods()

myClass.chosenMethod = { $0.method2(0, 1.0) }
//myClass.chosenMethod = { $0.methods[1](0, 1.0) }
myClass.execute()

#2

Через клоужеры так и не разобрался как сделать, зато нашел отличный вариант через селекторы. Т.к. perform(Selector) в swift не работает, если класс не наследовать от NSObject (чего не хотелось бы), то вот альтернатива, если кому интересно:

Thread(target: self, selector: mySelector, object: myParameters).main()

А отличный он потому, что обычно с селекторами не получается передать параметры, а тут это работает.

var myParameters: [Any]! = [2, "check", 5.0]
mySelector = getMethods()[0]

@objc func myFunc(_ parameters: [Any]!) {
...
}