【问题标题】:Call a method with dynamic class name in swift快速调用具有动态类名的方法
【发布时间】:2019-02-06 10:04:32
【问题描述】:

我们如何调用具有动态类名的类函数?

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

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 在运行时接受值

编辑:抱歉,我没有提到我正在寻找除面向协议的方法之外的任何其他方法。这更像是一个探索性问题,需要探索是否有更直接的方法/pod/库来实现这一目标。

【问题讨论】:

  • 我会尝试使用 doSomething() 向 Foo 和 Foobar 添加一个协议。然后制作一个 genericType 需要该协议 - 然后使用 genericType 作为参数。

标签: ios swift swift4.1


【解决方案1】:

我喜欢这个问题,因为它让我思考了一些问题。

我会把它分成几部分来回答。

第一

调用类函数

类函数基本上是一个Type methods,可以使用class上下文中的static字来实现。

考虑到这一点,您可以获得一个简单的解决方案,使用 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());
}

第三

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

类函数doSomething()

您不能简单地使用协议。因为协议不是一个类...... 所以编译器不允许这样做。

但这仍然是可能的,您可以通过使用 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】:

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

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)

【讨论】:

    【解决方案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)
    

    【讨论】:

    • 问题是关于类函数
    【解决方案4】:

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

    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)
    

    【讨论】:

    • 好的解决方案?
    【解决方案5】:

    看起来您正在搜索duck typing,而这在静态类型语言中更难实现(有一些例外,在链接的维基百科页面中列出)。

    这是因为动态调用方法需要了解目标对象的布局,因此要么继承声明方法的类,要么符合需要该方法的协议。

    从 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 版本中,无法同时使参数和方法动态化,需要设置一些共同点。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-05-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-12-24
      • 2023-03-25
      相关资源
      最近更新 更多