Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to invoke a class method using performSelector() on AnyClass in Swift?

In ObjC you could simply invoke a class method using the class method from NSObject.

[Machine performSelector:@selector(calculate:) withObject:num];

But how do you do this in Swift 2.2?

@objc(Machine) // put it here, so you can simply copy/paste into Playground
class Machine: NSObject {
    static func calculate(param: NSNumber) -> String {
        if param.integerValue > 5 {
            return "42"
        }
        return "42" // there is only 1 answer to all the questions :D
    }
}

if let aClass = NSClassFromString("Machine") {
    let sel = #selector(Machine.calculate(_:))
    let num = NSNumber(integer: 1337)
    let answer = aClass.performSelector(sel, withObject: num) // compiler error
    // let answer = aClass.calculate(num)                     // <-- this works
    print(answer)
}

With this code I'm getting the following compiler error:

error: cannot invoke 'performSelector' with an argument list of type '(Selector, withObject: NSNumber)'

What am I missing here?

like image 216
Buju Avatar asked Mar 24 '16 19:03

Buju


People also ask

What is a Swift class and object?

In this tutorial, we will learn about Swift Classes and we'll also learn to create objects with the help of examples. Swift is also an object-oriented programming language. And, like other oop languages, it also supports the concept of objects and classes. An object is simply a collection of data (variables) and methods (functions).

What types can define methods in Swift?

In Objective-C, classes are the only types that can define methods. In Swift, you can choose whether to define a class, structure, or enumeration, and still have the flexibility to define methods on the type you create.

What is the difference between Objective-C and Swift type methods?

Type methods are similar to class methods in Objective-C. The fact that structures and enumerations can define methods in Swift is a major difference from C and Objective-C. In Objective-C, classes are the only types that can define methods.

Do you have to have an interface in Swift?

Unlike other programming languages, Swift doesn’t require you to create separate interface and implementation files for custom structures and classes. In Swift, you define a structure or class in a single file, and the external interface to that class or structure is automatically made available for other code to use.


1 Answers

AnyClass does not conform to NSObjectProtocol out of the box. I had to cast aClass as NSObjectProtocol to use performSelector (performSelector:withObject: is bridged to Swift as a method on NSObjectProtocol):

Swift 3:

if let aClass = NSClassFromString("Machine") {
    let sel = #selector(Machine.calculate(param:))
    let num = NSNumber(value: 1337)

    if let myClass = aClass as? NSObjectProtocol {
        if myClass.responds(to: sel) {
            let answer = myClass.perform(sel, with: num).takeRetainedValue() // this returns AnyObject, you may want to downcast to your desired type
            print(answer) // "42\n"
        }
    }
}

Swift 2.x:

(aClass as! NSObjectProtocol).performSelector(sel, withObject: num) // Unmanaged<AnyObject>(_value: 42) 

A little bit safer:

if let aClass = NSClassFromString("Machine") {
    let sel = #selector(Machine.calculate(_:))
    let num = NSNumber(integer: 1337)

    if let myClass = aClass as? NSObjectProtocol {
        if myClass.respondsToSelector(sel) {
            let answer = myClass.performSelector(sel, withObject: num).takeUnretainedValue()
            print(answer) // "42\n"
        }
    }
}

performSelector returns an Unmanaged object, that's why takeUnretainedValue() (or optionally takeRetainedValue() if you want to transfer memory ownership) are required.

like image 119
JAL Avatar answered Nov 09 '22 15:11

JAL