Подтвердить что ты не робот

Вызов метода из строки в Swift

Иногда может оказаться полезным вызов метода из его имени (в формате String).
В Swift рекомендуется изменить поведение и использовать блокировки, чтобы что-то делать "динамически", поэтому, например, вы можете иметь словарь функций с именем в качестве ключа и реализацией в качестве значения.
Однако иногда вы просто хотите знать "как это сделать", и это и есть причина этого вопроса.
Итак, как динамически вызывать метод Swift, начинающийся с него именем в виде строки?

В Objective C это было просто:

[self performSelector:NSSelectorFromString(@"aSelector")];

Но performSelector запрещен в Swift Есть ли альтернатива?

4b9b3361

Ответ 1

В Swift вы должны использовать закрытие и изменить свой подход. Однако, если вы хотите использовать performSelector для динамического вызова метода с учетом только строковой подписи, хотя он не поддерживается изначально, я нашел, как это сделать.

Можно создать альтернативу C, чтобы выполнить выбор:

  • работает даже на родных быстрых классах (не objective-c)
  • принимает селектор из строки

Однако это не так просто реализовать полную версию, и необходимо создать метод в C.

в C мы имеем функцию dlsym(), которая возвращает указатель на функцию с символом char. Хорошо, прочитав этот интересный пост: http://www.eswick.com/2014/06/inside-swift/ Я узнал много интересного о быстрых событиях.

Быстрые методы экземпляра - это простые функции с определенной сигнатурой, например

_TFC14FirstSwiftTest12ASampleClass13aTestFunctionfS0_FT_CSo8NSString

где значение "self" передается как последний параметр

Короче говоря, вы можете вызвать его прямо со стороны c без какого-либо моста, достаточно перестроить правильную сигнатуру функции. В подписи выше указано название проекта (FirstSwiftTest) и длина (14), имя класса (ASampleClass) и длина (12), имя функции (aTestFunction) и длина (13), затем другие значения в качестве возвращаемого типа ecc ecc. Для получения дополнительной информации смотрите предыдущую ссылку

Функция выше, является следующим:

class ASampleClass
{
    func aTestFunction() -> NSString
    {
        println("called correctly")
        return NSString(string: "test")
    }

}

Ну, со стороны, мне удалось создать эту функцию

#include <stdio.h>
#include <dlfcn.h>

typedef struct objc_object *id;

id _performMethod(id stringMethod, id onObject)
{
    // ...
    // here the code (to be created) to translate stringMethod in _TFC14FirstSwiftTest12ASampleClass13aTestFunctionfS0_FT_CSo8NSString
    // ...

    id (*functionImplementation)(id);
    *(void **) (&functionImplementation) = dlsym(RTLD_DEFAULT, "_TFC14FirstSwiftTest12ASampleClass13aTestFunctionfS0_FT_CSo8NSString");

    char *error;

    if ((error = dlerror()) != NULL)  {
        printf("Method not found \n");
    } else {
        return functionImplementation(onObject); // <--- call the function
    }
    return NULL
}

И затем вызвал его на быстрой стороне

    let sampleClassInstance = ASampleClass()

    println(_performMethod("aTestFunction", sampleClassInstance))

В результате эта функция была напечатана в журнале:

правильно называется
тест

Так что не сложно создать альтернативу _performMethod() в C, которая:

  • автоматически создает подпись функции (поскольку она имеет логику: -)
  • управляет различными типами возвращаемых значений и параметрами

ИЗМЕНИТЬ

В Swift 2 (и, возможно, в Beta3, я не пробовал) Кажется, что performSelector() разрешен (и вы можете вызывать его только в подклассах NSObject). Изучение двоичного кода. Кажется, что теперь Swift создает статические функции, которые могут быть специально вызваны функцией performSelector.

Я создал этот класс

class TestClass: NSObject {
    func test() -> Void {
        print("Hello");
    }
}

let test = TestClass()
let aSel : Selector = NSSelectorFromString("test")
test.performSelector(aSel)

и теперь в бинарнике я нахожу

000000010026d830 t __TToFC7Perform9TestClass4testfT_T _

В настоящее время я не очень хорошо понимаю причины этого, но я буду исследовать далее

Ответ 2

Вы можете вызвать метод из строки следующим образом:

let selectorName = "name"
perform(Selector(selectorName))

Он доступен, только если ваш класс является подклассом NSObject

Ответ 3

версия swift3

class MyClass:NSObject
{
    required public  override init() { print("Hi!") }

    public func test(){
        print("This is Test")
    }
    public class func static_test(){
        print("This is Static Test")
    }
}


if let c: NSObject.Type = NSClassFromString("TestPerformSelector.MyClass") as? NSObject.Type{
   let c_tmp = c.init()
   c_tmp.perform(Selector("test"))
   c.perform(Selector("static_test"))
}