45

Calling a method from its name (in a String format) can be sometimes useful.
In Swift it is recomended to change behavior and to use closures to do something "dynamically", so for example you can have a dictionary of functions, with the name as the key, and the implementation as the value.
However, sometimes you want to simply know "how to do it", and this is the reason of this question.
So, how to call dynamically a Swift method starting from it's name as string?

In Objective C it was simple:

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

But performSelector is banned in Swift
Is there any alternative?

idmean
  • 14,540
  • 9
  • 54
  • 83
LombaX
  • 17,265
  • 5
  • 52
  • 77
  • 1
    Just wondering why you deleted your answer? Nothing wrong with Q&A – Tim Jun 16 '14 at 14:15
  • It has been deleted by a moderator, I don't know why, Now I'll ask him – LombaX Jun 16 '14 at 14:16
  • 6
    Swift is mostly a static language with strong typing and no real reflection. This kind of dynamic behavior is unsafe and thus to be avoided in Swift. Swift doesn't support it in any way right now. Also, it doesn't work well with ARC (even in Obj-C it triggers warning). You can make Obj-C workarounds in Swift but there is no real Swift solution because Swift doesn't like this kind of things. – Sulthan Jun 16 '14 at 14:18
  • answer came back. My mistake, I "spammed" copying and pasting the same answer on another post! – LombaX Jun 16 '14 at 14:25
  • @LombaX, I have not down-voted you, but I'm sure the reason is because your question clearly screams you have not read the the documentation about _Swift_, and to be fair, not everybody is patient to explain a thing here again if that has been already explained well in the official docs... and it was questioned many times in _stackoverflow_ already. so, the minimal amount of investigation on your side looks clearly to be missing in this topic. – holex Jun 16 '14 at 14:30
  • 4
    @holex , I've read all the docs and analyzed Swift in every detail ;-) and this is the reason why I wrote this Q&A (do you see? There is my answer below) with a solution that, by now, is not available on any other site. – LombaX Jun 16 '14 at 14:32
  • @LombaX, I'm far away to judge or lecture you, but in the last two weeks I literally have been reading all available docs of _Swift_ letter by letter, and I have not raised such question because there is a few other different code-patterns about how you can substitute such _Obj-C_ pattern in _Swift_, in spite of the `–performSelector:...` method-family is not available in _Swift_ directly. – holex Jun 16 '14 at 14:39
  • 1
    I know it!! Do you understand that the question has been posted only to attach THE ANSWER, in a Q&A style? Moreover, to confirm what you are saying, my question begans with this statement: "In Swift it is recomended to change behavior". But the meaning of the Q&A is different: understand *how it works* and *why* – LombaX Jun 16 '14 at 14:42
  • @LombaX, that just makes your question more senseless, if you already know one of the possible answers / solutions. you know that you cannot call `–performSelector:...` method-family in _Swift_ and there is no substitute method which you can use it instead, because that is **unsafe**. you know, you need to find another way to achieve something similar, but you still ask how you can do such a thing which is unsafe and is not supported directly in _Swift_. that is also a possible reason of why someone has downvoted you... I'm not helpful, I know. I quit. – holex Jun 16 '14 at 14:51
  • 8
    @holex, Q+A style questions are encouraged in the Stackoverflow community, he specifically asked the question to provide an answer himself for people who maybe wondering the same. – Tim Jun 16 '14 at 16:14

3 Answers3

37

In Swift, you should use closures and change your approach. However, if you want to use performSelector to dynamically call a method given only it's String signature, altough it's not supported natively, I've found how to do it.

It is possible to create a C alternative to performSelector that:

  • works even on native swift classes (non objective-c)
  • takes a selector from string

However it's not so straightforward to implement a complete version of it, and it's necessary to create the method in C.

in C we have dlsym(), a function that returns a pointer to a function given the char symbol. Well, reading this interesting post: http://www.eswick.com/2014/06/inside-swift/ I've learned a lot of interesting things about swift.

Swift instance methods are plain functions with a specific signature, like this

_TFC14FirstSwiftTest12ASampleClass13aTestFunctionfS0_FT_CSo8NSString

where the "self" value is passed as the last parameter

in short you can call it directly from the c side without any kind of bridging, it is sufficient to rebuild the correct function signature. In the signature above, there is the name of the project (FirstSwiftTest) and the lenght (14), the name of the class (ASampleClass) and the lenght (12), the name of the function (aTestFunction) and the lenght (13), then other values as the return type ecc ecc. For other details look at the previous link

The function above, is the representation of this:

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

}

Well, on the c side, I was able to create this function

#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
}

And then called it on the swift side

    let sampleClassInstance = ASampleClass()

    println(_performMethod("aTestFunction", sampleClassInstance))

The function resulted in these statement printed on the log:

called correctly
test

So it should be not so difficult to create a _performMethod() alternative in C that:

  • creates automatically the function signature (since it seems to have a logic :-)
  • manages different return value types and parameters

EDIT

In Swift 2 (and maybe in Beta3, I didn't try) It seems that performSelector() is permitted (and you can call it only on NSObject subclasses). Examining the binary, It seems that now Swift creates static functions that can be specifically called by performSelector.

I created this class

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

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

and now in the binary I find

000000010026d830 t __TToFC7Perform9TestClass4testfT_T_

At this time, I don't understand well the reasons behind this, but I'll investigate further

LombaX
  • 17,265
  • 5
  • 52
  • 77
  • 2
    might want to clarify that the class must inherit from `NSObject` in order to get the `performSelector` method available. – Nick Molyneux Jul 20 '16 at 22:42
8

You could call a method from a String this way:

let foo = <some NSObject subclass instance>
let selectorName = "name"
foo.perform(Selector(selectorName))

It is available only when your foo class is subclass of NSObject

David H
  • 40,852
  • 12
  • 92
  • 138
Andrew Bogaevskyi
  • 2,251
  • 21
  • 25
  • Yes, now it is possible. At the time of the answer, performSelector was "banned". It seems that now Swift creates a symbol (a static function) for each possible performSelector. I update my answer – LombaX Jul 19 '16 at 08:25
  • `perform` isn't a keyword. Is this correct or am I misunderstanding something? – Nick Molyneux Jul 20 '16 at 22:43
  • `perform` is a method of `NSObject` (Swift 3) https://developer.apple.com/reference/objectivec/nsobject/1415652-performselector – Andrew Bogaevskyi Jul 21 '16 at 07:27
5

swift3 version

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"))
}
Community
  • 1
  • 1
gakaki gakaki
  • 51
  • 1
  • 1