Fork me on GitHub

Swift4.0中使用NSSelectorFromString()

Swift4.0中调用Selector采用#selector(xxx)的方式,而且要求被调用的Selector前面加上@objc修饰符。否则编译器报错 如图所示。

编译器报错

有时候项目中会有一些特殊的需求,需要用到NSSelectorFromString(),然后使用 perform(_ aSelector: Selector!) -> Unmanaged! 进行方法调用,如下代码。

1
2
3
4
let sel: Selector = NSSelectorFromString("doSomething")
if (self.responds(to: sel)) {
self.perform(sel)
}

这样调用的话,需要在实现方法的前面也加上@objc修饰符,否则编译可以通过,但是调用没有效果。

1
2
3
4
5
// 代码示例
@objc
func doSomething() -> Void {
print("doSomething")
}

看起来和OC中使用差别并不大,但是如果被调用的方法带参数的话,情况就不一样了。由于Swift的语法特性,带参数的func有3种写法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@objc
func doSomething1(name: String?, type: String?) -> Void {
print("doSomething1:")
}

@objc
func doSomething2(with name: String?, type: String?) -> Void {
print("doSomething2:")
}

@objc
func doSomething3(_ name: String?, type: String?) -> Void {
print("doSomething3:")
}

这样一来,NSSelectorFromString(“xxx”)中,方法名该如何填写呢?

这里有一个思路,既然方法名前面都有@objc修饰符,那我们应该是可以通过runtime,将这些方法获取并打印出来,那么方法名就一览无余啦。

接下来在Swift中建一个OC类文件做一个工具类,之后再桥接上头文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 导入runtime头文件
#import <objc/runtime.h>

@implementation MethodTool

+ (void)printMothListWithObj:(id)obj {

unsigned int mothCout_f =0;

Method* mothList_f = class_copyMethodList([obj class], &mothCout_f);

for (int i = 0; i < mothCout_f; i ++) {

Method temp_f = mothList_f[i];
IMP imp_f = method_getImplementation(temp_f);
SEL name_f = method_getName(temp_f);

const char *name_s = sel_getName(method_getName(temp_f));
int arguments = method_getNumberOfArguments(temp_f);
const char *encoding = method_getTypeEncoding(temp_f);

NSLog(@"方法名:%@,参数个数:%d,编码方式:%@",[NSString stringWithUTF8String:name_s], arguments, [NSString stringWithUTF8String:encoding]);
}

free(mothList_f);
}

@end

工具搞好了,创建一个Swift对象,用工具类打印出方法,得出如下结果:

1
2
3
2018-03-13 18:03:51.163992+0800 SwiftOCDemo[92116:21107487] 方法名:doSomething1WithName:type:,参数个数:4,编码方式:v32@0:8@16@24
2018-03-13 18:03:51.164126+0800 SwiftOCDemo[92116:21107487] 方法名:doSomething2With:type:,参数个数:4,编码方式:v32@0:8@16@24
2018-03-13 18:03:51.164265+0800 SwiftOCDemo[92116:21107487] 方法名:doSomething3:type:,参数个数:4,编码方式:v32@0:8@16@24

果然,Swift4中带参数的方法名在写法不同的情况下,生成的方法名是不同的,最终我们根据输出结果,就可以放心调用啦!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
let sel1 = NSSelectorFromString("doSomething1WithName:type:")
if (self.responds(to: sel1)) {
self.perform(sel1, with: "", with: "")
}

let sel2 = NSSelectorFromString("doSomething2With:type:")
if (self.responds(to: sel2)) {
self.perform(sel2, with: "", with: "")
}

let sel3 = NSSelectorFromString("doSomething3:type:")
if (self.responds(to: sel3)) {
self.perform(sel3, with: "", with: "")
}

虽然需求得以实现,不过在此还是提醒大家,Swift语言的初衷是为大家提供方便并且安全的开发方式,这种通过NSSelectorFromString(“xxx”)的方式显然是违背设计原则的,大家在开发的过程中尽量避免使用这种方式。当然,遇到坑爹的需求就另说了,毕竟技术要服务于业务嘛。。。

还有一点就是,我在Swift提供的perform()方法中只发现了这几个func

perform

如果你要调用的方法多于两个参数的话,那就得考虑一下用何种方案了。

------------- 本文结束感谢您的阅读 -------------