快速调用具有动态类名的方法

时间:2019-02-06 10:04:33

标签: ios swift swift4.1

我们如何使用动态类名来调用类函数?

假设以下示例中,我有两个类的方法具有相同的签名

class Foo{
   class func doSomething()

}

class Foobar {
   class func doSomething()
}

class ActualWork{
   //call following method with a variable type so that it accepts dynamic class  name
   func callDynamicClassMethod(x: dynamicClass)
    x.doSomething() 
  }

该如何实现,以便x在运行时接受值

编辑:对不起,我错过了提及我正在寻找除面向协议的方法以外的任何其他方法的信息。这更多是一个探索性的问题,以探索是否有更直接的方法/库/图书馆来实现这一目标。

5 个答案:

答案 0 :(得分:7)

面向泛型和协议的编程将完成这项工作:

protocol Doable {

    static func doSomething()
}

class Foo: Doable {

    static func doSomething() {
        debugPrint("Foo")
    }
}

class Foobar: Doable {

    static func doSomething() {
        debugPrint("Foobar")
    }
}

class ActualWork {

    func callDynamicClassMethod<T: Doable>(x: T.Type) {
        x.doSomething()
    }
}

let work = ActualWork()

work.callDynamicClassMethod(x: Foo.self)
work.callDynamicClassMethod(x: Foobar.self)

答案 1 :(得分:6)

我喜欢这个问题,因为它使我想起了盒子外面的一小部分。

我将其分为几部分来回答。

第一

  

调用类函数

Class函数基本上是Type methods,可以使用static上下文中的class单词来实现。

考虑到这一点,您可以使用protocol并像下面这样传递类引用(符合该协议)来获得简单的解决方案:

protocol Aaa{
   static func doSomething();
}
class Foo : Aaa{
  static func doSomething() {
    print("Foo doing something");
  }
}
class FooBar : Aaa{
  static func doSomething() {
    print("FooBar doing something");
  }
}

class ActualWork{

  //Using class (static) method
  func callDynamicClassMethod <T: Aaa> (x: T.Type) {
    x.doSomething();
  }
}


//This is how you can use it
func usage(){
    let aw = ActualWork();

    aw.callDynamicClassMethod(x: Foo.self);
    aw.callDynamicClassMethod(x: Foo.self);
}

第二

如果您在类上下文中确实不需要该方法,则可以考虑使用实例方法。在这种情况下,解决方案将更加简单,如下所示:

protocol Bbb{
  func doSomething();
}
class Bar : Bbb{
  func doSomething() {
    print("Bar instance doing something");
  }
}
class BarBar : Bbb{
  func doSomething() {
    print("BarBar instance doing something");
  }
}
class ActualWork{
  //Using instance (non-static) method
  func callDynamicInstanceMethod <T: Bbb> (x: T){
    x.doSomething();
  }
}
//This is how you can use it
func usage(){
   let aw = ActualWork();
    aw.callDynamicInstanceMethod(x: Bar());
    aw.callDynamicInstanceMethod(x: BarBar());
}

第三

如果您需要使用class func语法,如OP最初那样:

  

func类doSomething()

您不能简单地使用协议。因为协议不是一类... 因此,编译器将不允许它。

Cannot declare class function inside protocol

但是仍然有可能,您可以通过使用 SelectorNSObject.perform方法

像这样:

class ActualWork : NSObject{

    func callDynamicClassMethod<T: NSObject>(x: T.Type, methodName: String){
        x.perform(Selector(methodName));
    }

}

class Ccc : NSObject{
    @objc class func doSomething(){
        print("Ccc class Doing something ");
    }

}

class Ddd : NSObject{
    @objc class func doSomething(){
        print("Ccc class Doing something ");
    }

    @objc class func doOther(){
        print("Ccc class Doing something ");
    }
}

//This is how you can use it
func usage() {
    let aw = ActualWork();

    aw.callDynamicClassMethod(x: Ccc.self, methodName: "doSomething");
    aw.callDynamicClassMethod(x: Ddd.self, methodName: "doSomething");
    aw.callDynamicClassMethod(x: Ddd.self, methodName: "doOther");

}

答案 2 :(得分:3)

您可以在协议的帮助下实现这一目标

 protocol common {
   static func doSomething()
}


class Foo : common{
   static  func doSomething() {
        print("Foo")
    }
}

class Foobar : common {
   static func doSomething() {
        print("Foobar")
    }
}


class ActualWork{
    //call following method with a variable type so that it accepts dynamic class  name
    func callDynamicClassMethod(x: common.Type) {
            x.doSomething()
    }
}

let fooObj : common = Foo()
let Foobarobj : common = Foobar()

let workObk = ActualWork()
workObk.callDynamicClassMethod(x:Foo.self)
workObk.callDynamicClassMethod(x:Foobar.self)

答案 3 :(得分:3)

我认为,有三种解决方案。我在下面分享了一个示例。

  1. 使用具有“ doSomething()”功能要求的“协议”。
  2. 创建一个将函数定义作为参数的函数。
  3. 使用反射。您可以使用EVReflection很好的Api进行反射。

示例代码:

protocol FooProtocol {
    static func doSomething()
}

class Foo: FooProtocol {
    class func doSomething() {
        print("Foo:doSomething")
    }
}

class Foobar: FooProtocol {
    class func doSomething() {
        print("Foobar:doSomething")
    }
}

class ActualWork {
    func callDynamicClassMethod<T: FooProtocol>(x: T.Type) {
        x.doSomething()
    }

    func callDynamicClassMethod(x: @autoclosure () -> Void) {
        x()
    }

    func callDynamicClassMethod(x: () -> Void) {
        x()
    }
}

ActualWork().callDynamicClassMethod(x: Foo.self)
ActualWork().callDynamicClassMethod(x: Foobar.self)
print("\n")
ActualWork().callDynamicClassMethod(x: Foo.doSomething())
ActualWork().callDynamicClassMethod(x: Foobar.doSomething())
print("\n")
ActualWork().callDynamicClassMethod(x: Foo.doSomething)
ActualWork().callDynamicClassMethod(x: Foobar.doSomething)

答案 4 :(得分:0)

看起来您正在搜索duck typing,而使用静态类型的语言(在链接的Wikipedia页面中列出了一些例外)很难做到这一点。

这是因为动态调用方法需要有关目标对象布局的知识,因此要么继承声明该​​方法的类,要么遵守需要该方法的协议。

从Swift 4.2开始,开始引入dynamic member lookup,还有另一种方法可以解决您的问题,但这还涉及一些仪式:

// This needs to be used as base of all classes that you want to pass
// as arguments 
@dynamicMemberLookup
class BaseDynamicClass {
    subscript(dynamicMember member: String) -> () -> Void {
        return { /* empty closure do nothing */ }
    }
}

// subclasses can choose to respond to member queries any way they like
class Foo: BaseDynamicClass {
    override subscript(dynamicMember member: String) -> () -> Void {
        if member == "doSomething" { return doSomething }
        return super[dynamicMember: member]
    }

    func doSomething() {
        print("Dynamic from Foo")
    }

}

class Bar: BaseDynamicClass {
    override subscript(dynamicMember member: String) -> () -> Void {
        if member == "doSomething" { return doSomething }
        return super[dynamicMember: member]
    }

    func doSomething() {
        print("Dynamic from Bar")
    }
}

func test(receiver: BaseDynamicClass) {
    receiver.doSomething()
}

test(receiver: Bar()) // Dynamic from Bar

总而言之,在当前的Swift版本中,无法同时使参数和方法动态化,需要设置一些共同点。