Приветствую! Пытаюсь разобраться с 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()