在 swift 中调用具有动态 class 名称的方法
Call a method with dynamic class name in swift
我们如何使用动态 class 名称调用 class 函数?
假设下面的示例中我有两个 class 方法具有相同的签名
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 在 运行 时间接受值
编辑:抱歉,我没有提到我正在寻找除面向协议的方法之外的任何其他方法。这更像是一个探索性的问题,探索是否有更直接的approach/pods/libraries来实现这一点。
您可以借助协议
实现这一目标
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)
面向泛型和协议的编程将完成这项工作:
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)
我认为,有三种解决方案。我在下面分享了一个示例。
- 使用具有 "doSomething()" 功能要求的 "protocol"。
- 创建一个将函数定义作为参数的函数。
- 使用反射。您可以使用 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)
我喜欢这个问题,因为它让我跳出框框思考。
我会分成几个部分来回答。
第一
call class functions
Class 函数基本上是一个 Type methods,可以使用 class
上下文中的 static
词来实现。
考虑到这一点,您可以获得一个简单的解决方案,使用 protocol 并传递 class 引用(符合该协议),如下所示:
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);
}
第二
如果您真的不需要 class 上下文中的方法,您可以考虑使用实例方法。在那种情况下,解决方案会更简单,像这样:
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 最初那样:
class func doSomething()
您不能简单地使用协议。因为协议不是 class...
所以编译器不会允许它。
但这仍然是可能的,您可以通过使用
Selector with NSObject.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");
}
看起来您正在搜索 duck typing,而这在静态类型语言中更难实现(除了一些例外,在链接的维基百科页面中列出)。
这是因为动态调用方法需要了解目标对象的布局,因此要么继承声明该方法的 class,要么遵守需要该方法的协议。
从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 版本中,无法让参数和方法都动态化,需要设置一些共同点。
我们如何使用动态 class 名称调用 class 函数?
假设下面的示例中我有两个 class 方法具有相同的签名
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 在 运行 时间接受值
编辑:抱歉,我没有提到我正在寻找除面向协议的方法之外的任何其他方法。这更像是一个探索性的问题,探索是否有更直接的approach/pods/libraries来实现这一点。
您可以借助协议
实现这一目标 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)
面向泛型和协议的编程将完成这项工作:
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)
我认为,有三种解决方案。我在下面分享了一个示例。
- 使用具有 "doSomething()" 功能要求的 "protocol"。
- 创建一个将函数定义作为参数的函数。
- 使用反射。您可以使用 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)
我喜欢这个问题,因为它让我跳出框框思考。
我会分成几个部分来回答。
第一
call class functions
Class 函数基本上是一个 Type methods,可以使用 class
上下文中的 static
词来实现。
考虑到这一点,您可以获得一个简单的解决方案,使用 protocol 并传递 class 引用(符合该协议),如下所示:
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);
}
第二
如果您真的不需要 class 上下文中的方法,您可以考虑使用实例方法。在那种情况下,解决方案会更简单,像这样:
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 最初那样:
class func doSomething()
您不能简单地使用协议。因为协议不是 class... 所以编译器不会允许它。
但这仍然是可能的,您可以通过使用 Selector with NSObject.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");
}
看起来您正在搜索 duck typing,而这在静态类型语言中更难实现(除了一些例外,在链接的维基百科页面中列出)。
这是因为动态调用方法需要了解目标对象的布局,因此要么继承声明该方法的 class,要么遵守需要该方法的协议。
从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 版本中,无法让参数和方法都动态化,需要设置一些共同点。