2016-03-24 18 views
6

W ObjC można po prostu wywołać metodę klasy, używając metody klasy od NSObject.Jak wywołać metodę klasy za pomocą performSelector() na AnyClass w Swift?

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

Ale jak to zrobić w 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) 
} 

Z tym kodem Ja otrzymuję następujący błąd kompilatora:

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

Co ja tu brakuje?

+1

Z pewnością nie jest bezcelowe, a co jeśli chcesz używać klasy z zewnętrznego źródła? NA PRZYKŁAD. czytając nazwę klasy z łańcucha w pliku plist, czy napiszesz warunek if na podstawie każdego możliwego obiektu? – Knight0fDragon

+0

Powiązane: [Dostęp do prywatnej funkcji UIKit bez użycia nagłówka pomostowego] (http://stackoverflow.com/q/35183818/2415822) – JAL

Odpowiedz

11

AnyClass nie odpowiada NSObjectProtocol się z pudełko. Miałem oddać aClass jak NSObjectProtocol użyć performSelector (performSelector:withObject: mostkuje Swift jako sposób na 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 trochę bezpieczniejsze:

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 zwraca obiekt Unmanaged, dlatego wymagane jest takeUnretainedValue() (lub opcjonalnie takeRetainedValue(), jeśli chcesz przenieść własność pamięci).

+0

To jest zabawne, ponieważ odlewanie go jako? AnyObject działał dla mnie – Knight0fDragon

+0

@ Knight0fDragon Przesyłanie do 'AnyObject' będzie działało, ponieważ' AnyClass' jest naprawdę typem dla metacypu 'AnyObject.Type'. Prawdopodobnie dostałeś ostrzeżenie, że odejście się udaje, ponieważ jest tego samego typu. Prawdopodobnie nie jest to dobry pomysł, aby rzucić klasę na obiekt. – JAL

+0

@ Knight0fDragon Możesz używać 'performSelector' tylko w typach C, a nie w typach Swift. Spróbuj. Właśnie dlatego sprawdzam zgodność 'NSObjectProtocol'. – JAL

-1

Jeśli chcesz wykonać obliczyć na komputerze, po prostu zrobić:

Machine.calculate(NSNumber(integer: 1337)) 

Jeśli trzeba zadzwonić wykonać selektora, a następnie wykonaj:

if let aClass = NSClassFromString("Machine") as? AnyObject 
{ 
    let sel = #selector(Machine.calculate(_:)) 
    let num = NSNumber(integer: 1337) 
    let answer = aClass.performSelector(sel, withObject: num) 
    print(answer) 
} 
+0

Kod Swift jest ustalony – Knight0fDragon