0%

Dart里坑人的extension

Dart里坑人的extension

先随便去哪里run下面的代码(图简便直接跑DartPad

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void main() {
final x = _111();
print('methods inside class');
for (int i = 0; i < 5; i++) {
print(x.helloworld.hashCode);
}
print('methods inside extension of class');
for (int i = 0; i < 5; i++) {
print(x.helloworldOnExtension.hashCode);
}
}

class _111 {
void helloworld() {}
}

extension on _111 {
void helloworldOnExtension() {}
}

看输出你发现了什么?

  1. methods inside class的地址(hashcode)是不会变的
  2. methods inside extension of class的地址是会变的,这说明dart里的extension中方法的实现机制可能是:在引用时创建一个匿名函数,临时分配地址

为什么坑人

如果想用extension做代码拆分的任务(虽然也有可能是这个初衷就存在问题,本来就不应该用extension做代码拆分),往extension里写入了一些你本来认为是「静态函数」的函数,认为引用的地址不会变。

比如

1
2
3
4
somePublisher.addListener(helloworldOnExtension)

// then
somePublisher.removeListener(helloworldOnExtension) // doesn't work

此时removeListener不会起作用,因为此时的helloworldOnExtension又是一个新的地址。

结论

少用extension(

关于dart里面的方法调度

如果将最开始的代码第二行改为

1
final dynamic x = _111();

运行时会抛出error:

1
Uncaught TypeError: x.get$helloworldOnExtension is not a functionError: TypeError: x.get$helloworldOnExtension is not a function

这也侧面印证了dart里extension函数是动态生成的,并不存在于函数表里。

来到dart类里面的函数、变量查找机制。

即使是dynamic类型只要调用的方法名存在与该类,dart就能找到并调用对应的方法。

这说明dart可能用的是和OC一样的消息传递调用方法的机制,如上也可以看出来,通过将helloworldOnExtension发送给x.get来完成方法调用。