____|                                    \                                 
|    |   | _  / _  /   _ \   __|        _ \    __ `__ \    _ \    __|  _ \ 
__|  |   |   /    /    __/  |          ___ \   |   |   |  (   |  |     __/ 
_|   \__,_| ___| ___| \___| _|        _/    _\ _|  _|  _| \___/  _|   \___| 
                                                                                    
<- Back
Oct 1, 2025

50 ways to call your function

env_diagram

Introduction

Hello,
In this post I will cover a new tool for generating reflection code for ObjC/Swift.
Reflection could be defined as a generic way to trigger methods by their name. If we want to get more technical, it means obtaining the address of the implementation of a method by its name.
While the concept is not unique to ObjC, this is the focus and purpose of this tool.
To be more precise: this blog explores the different ways reflection can be used for triggering ObjC methods. So, how many ways can there be to trigger just one ObjC function? Apparently, not as few as one would think, and it turns out that there are more permutations than ways to leave your lover.

This blog post will introduce reflection basics, then move to advanced breakdown and options, and will end with instructions on how to use the tool. If you are not new to ObjC reflection you can skip to the breakdown and read from there.
Note that calling a method with reflection can be done using both ObjC and Swift. The callee, on the other hand, is always an ObjC method, or even an NSObject's child method.

Terminology

Callee - The ObjC method (function) that we want to call using reflection. For example, a method like -(id)convertIntToNSNumber:(int)value;

Reflection - The ability to find and call a method using a string that represents its name at runtime. In the example above, we want to be able to call the method by providing the string "convertIntToNSNumber:" to our reflection code

Selector - a struct (SEL) that represents the name of a method, which for our needs is equivalent to a string.

Class Method - A method that belongs to the class (a static method) that can be called without creating an instance of the class. The declaration may look like this: +(id)myFunc; and triggering it directly like so: id result = [MyClass myFunc];

Instance Method - A method that is called upon an instance of the class. The declaration may look like this: -(void)myFunc:(id)arg1; and triggering it directly like so: [myInstance myFunc:@"myParam"];

Target - The object that the callee belongs to. It may be a class or an instance, depending on the case (class for class method and instance for instance method)

The Simplest Example

Suppose this is our callee and its class
//MyClass.h @interface MyClass +(void)myMethod; @end // MyClass.m @implementation MyClass +(void)myMethod { NSLog(@"MyMethod was called"); }
Calling the method directly (without reflection) will probably look like this:

[MyClass myMethod];

Calling the method using reflection may look like this:

[MyClass performSelector:@selector(myMethod)];

This may look a bit confusing because we didn't really pass a string, but we still created a selector and triggered the method indirectly. If we would have wanted to use a real string, this may fit better:

[MyClass performSelector:NSSelectorFromString(@"myMethod")];

Breakdown

If we look at this last example:[MyClass performSelector:NSSelectorFromString(@"myMethod")];we can split it to several parts.

The first partMyClassis the class we want to call (also known as the target). The target can be obtained directly or from a string as well. There are different ways to obtain a class, such as usingNSClassFromStringorobjc_getClass

The second part, which is at the end of the line, creates the selector from a string. There are more ways to create a selector, such as#selectorin Swift, orsel_registerName, which returns a selector if one already exists.

The final part in this simple example is the way we trigger the selector. Once the target and selector are obtained, there are several ways to trigger them. In this exampleperformSelector:is used, but other options also exist such as NSInvocation / IMP / objc_msgSend.

Each method has some limitations and different availabilities depending on several parameters such as:

- The language we are calling from (objc/swift)
- The number of parameters the callee has
- The type of parameters
- The return value

This is actually where the tool gets handy, because it not only generates the desired code, it also sets the limits of your selections

Target

Obtaining the target for a class method can be done from a string. The NSClassFromString accepts anNSString, and returns aClass.
Alternativelyobjc_getClasscan be called with a C string which returns the same result. Class myClass = NSClassFromString(@"MyClassName"); // or Class myClass = objc_getClass("MyClassName");
This createsmyClasswhich can be used as a target, for example: [myClass performSelector:@selector(myMethod)];

When the callee is an instance method, the target needs to be an instance. Such an instance can be created directly, or using reflection as well.
Assume the following code: // The method we want to trigger: -(NSString*)userUUID; // the default initializer takes care of populating the UUID, so this should work: id myInstance = [User new]; NSString* theUUID = [myInstance userUUID]; // but if we want to use reflection: // Option A: id myInstance = [NSClassFromString(@"User") performSelector:NSSelectorFromString(@"new")]; NSString* theUUID = [myInstance performSelector:NSSelectorFromString(@"userUUID")]; // Option B: id myInstance = [objc_getClass("User") performSelector:sel_registerName("new")]; NSString* theUUID = [myInstance performSelector:sel_registerName("userUUID")]; So if we apply the concepts that we learned so far, we know that there is more than one way for obtaining the class, and more than one way to obtain the selector. Options A and B can both work, and even interchange the class/selector creation methods. While both A and B useperformSelector:you probably know by now that this is just one of several ways to invoke a selector upon a target

Before advancing to triggering, return type and arguments considerations, note that you can call these ObjC callees from Swift as well: // consider the same callee -(NSString*)userUUID; // calling it from Swift code let myInstance = User() let mySelector = Selector("userUUID") if let result = myInstance.perform(mySelector)?.takeUnretainedValue() as? NSString { print("new user's uuid: \(result)") }

Generator 101

This last Swift example can serve us as an introductory paragraph for using the generator.

We select the language as Swift, because we want to call the callee from Swift.
We select the instance method option because our callee starts with a '-'.
We select theSelectoroption for creating the selector. This option does not exist in ObjC.
We select the return value asNSObject, which is the base class of most NS* classes, including NSString
We select number of parameters as none.
We select the triggering method asperform, which is the Swift equivalent ofperformSelector


The generator UI now looks like the following:

tool_basics

The top part(1)represents the selections mentioned above.
The middle part(2)contains code for testing or illustrating the implementation which can be called using the generated code(3)
The middle part can also be copied to the relevant locations in the test app (fiftyWays)
The declaration will be added to the header file under the @interface, and the implementation in the .m file under the @implementation.
The last part(3)contains the generated code that calls this method via reflection.
In the test app this will be placed in the relevant caller, depending on the language, under thecallmethod

To illustrate:

declaration in the callee header file
header_file

implementation in the callee .m file
header_file

And calling from Swift
header_file

To see the result we can run in the simulator or Xcode's canvas
header_file

Advanced

Creating a selector and calling performSelectoris quite straightforward, but what about a more complex callee?

performSelectorcomes with two variants:
// objc - (id)performSelector:(SEL)aSelector; - (id)performSelector:(SEL)aSelector withObject:(id)object; - (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2; //swift func perform(_ aSelector: Selector!) -> Unmanaged! func perform(_ aSelector: Selector!, with object: Any!) -> Unmanaged! func perform(_ aSelector: Selector!, with object1: Any!, with object2: Any!) -> Unmanaged!
These declarations are taken from the NSObject protocols (ObjC and Swift)
This means you can call an ObjC method that has 2 arguments:
// method declaration +(BOOL) methodNameWith:(id)firstArg and:(id)secondArg; // calling it from swift let myClass = NSClassFromString("ClassName") let mySelector = Selector("methodNameWith:and:") if let result = myClass.perform(Selector:mySelector, with:NSString("myFirstArg"), with:NSString("mySecondArg")) as? Bool {...}

When the callee has more than two parameters,performSelectoris no longer an option.
This is where other APIs can be used:

IMP,for example, can be set with the implementation address (as a pointer), and triggered c-style,
id myClass = NSClassFromString("ClassName"); SEL mySelector = NSSelectorFromString(@"methodNameWith:and:and:"); IMP myImpl = [myClass methodForSelector:mySelector]; int (*myFunc) (id, SEL, id, id, id) = (int (*) (id, SEL, id, id, id))myImpl; int result = myFunc(myClass, mySelector, @"myFirstArg", @"mySecondArg", @"myThirdArg");

Alternatively,NSInvocationcan handle multiple arguments, invoke the method, and handle the return value.
id myClass = NSClassFromString("ClassName"); SEL mySelector = NSSelectorFromString(@"methodNameWith:and:and:"); NSMethodSignature *signature = [instance methodSignatureForSelector:mySelector]; NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature]; [invocation setSelector:mySelector]; [invocation setTarget:instance]; id firstArg = @"myFirstArg"; // or any other NSObject id secondArg = @"mySecondArg"; // or any other NSObject id thirdArg = @"myThirdArg"; // or any other NSObject [invocation setArgument:&firstArg atIndex:2]; // first arg is at index 2 [invocation setArgument:&secondArg atIndex:3]; [invocation setArgument:&thirdArg atIndex:4]; [invocation invoke]; int result; [invocation getReturnValue:&result];

objc_msgSendis yet another option for calling the method c-style with the same format.
This method (as well asIMP) works well for one-liner fans:
id myInstance = [MyClass new]; SEL mySelector = NSSelectorFromString(@"methodNameWith:and:and:"); BOOL result = ((BOOL (*) (id, SEL, float, id, id)) objc_msgSend)(myInstance, mySelector, 0.99, @"mySecondArg", @"myThirdArg"); /* Alternatively if you don't like the one liner approach BOOL (*myFunc) (id, SEL, float, id, id) = (BOOL (*) (id, SEL, float, id, id))objc_msgSend; BOOL result = myFunc(myInstance, mySelector, 0.99, @"mySecondArg", @"myThirdArg"); */
Note thatIMPis accessible via Swift, yet the other two are only available when calling from objc.

API Headers

To conclude the post, I encourage you to look for the relevant API in the header files.
This can help you better understand how everything is declared and can be used in case the generator produces some errors.
In some cases the header files need to be imported to the project. Some examples:

Foundation/NSObjCRuntime.h // objc FOUNDATION_EXPORT Class _Nullable NSClassFromString(NSString *aClassName); // swift public func NSClassFromString(_ aClassName: String) -> AnyClass? objc/runtime.h // objc OBJC_EXPORT Class _Nullable objc_getClass(const char * _Nonnull name) // swift public func objc_getClass(_ name: UnsafePointer) -> Any!

Conclusion

Well, as we have seen, there are multiple ways to trigger ObjC functions with reflection. This can be done from ObjC or Swift, and depends on the callee's format.
You may ask yourself why do we need so many ways if using just 2 can work just as well?
This is very true, but still fun, educational, and useful if some of the APIs will change in the future.


Concluding with the links once again:
generator
repo
More reading:
Apple, Hesry, Wheeler, ko9