2016-03-24 17 views
6

In ObjC können Sie einfach eine Klassenmethode mit der Klassenmethode von NSObject aufrufen.Wie ruft man eine Klassenmethode mit performSelector() auf AnyClass in Swift auf?

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

Aber wie machst du das 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) 
} 

Mit diesem Code, den ich die folgende Compiler-Fehler bekommen:

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

Was soll ich hier fehlt?

+1

Es ist definitiv nicht sinnlos, was ist, wenn Sie die Klasse von einer externen Quelle verwenden möchten? Z.B. Lesen Sie den Klassennamen aus einer Zeichenkette in einem Plist, werden Sie eine if-Bedingung basierend auf jedem möglichen Objekt schreiben? – Knight0fDragon

+0

Related: [Zugriff auf private UIKit-Funktion ohne Verwendung von Bridging-Header] (http://stackoverflow.com/q/35183818/2415822) – JAL

Antwort

11

AnyClass nicht NSObjectProtocol aus dem nicht entspricht Box. Ich hatte aClass als NSObjectProtocol zu gieße verwenden performSelector (performSelector:withObject: ist als ein Verfahren auf NSObjectProtocol zu Swift gebrückt):

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 ein wenig sicherer:

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 gibt ein Objekt Unmanaged zurück, deshalb sind takeUnretainedValue() (oder optional takeRetainedValue(), wenn Sie die Speichereigentümer übertragen möchten) erforderlich.

+0

Das ist lustig, weil es als Casting? AnyObject arbeitete für mich – Knight0fDragon

+0

@ Knight0fDragon Casting zu 'AnyObject' funktioniert, weil' AnyClass' wirklich eine Typalias für den Metatyp 'AnyObject.Type' ist. Sie haben wahrscheinlich eine Warnung erhalten, dass die Cast Aways erfolgreich sind, da sie vom selben Typ sind. Wahrscheinlich keine gute Idee, eine Klasse auf ein Objekt zu übertragen. – JAL

+0

@ Knight0fDragon Sie können 'performSelector' nur für Objective-C-Typen verwenden, nicht für reine Swift-Typen. Versuch es. Aus diesem Grund überprüfe ich die "NSObjectProtocol" -Konformität. – JAL

-1

Wenn Sie berechnen auf der Maschine ausführen möchten, gehen Sie einfach:

Machine.calculate(NSNumber(integer: 1337)) 

Wenn Sie Wähler durchführen müssen rufen, dann tun:

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

Swift-Code ist behoben – Knight0fDragon