译自:https://www.mikeash.com/pyblog/friday-qa-2014-08-15-swift-name-mangling.html
Friday Q&A 2014-08-08: Swift Name Mangling
byGwynne Raskind
自从我写上一篇的周五问答文章,已经过去了很长一段时间,但今天,我继续写一篇周五问答的文章,用一种全新的理念介绍一个全新的主题:Swift语言。在过去的几篇文章中, Mike一步一步深入的介绍了Swift的内部结构是什么样子,但他也只是稍微触及到了程序链接时使用的Swift二进制文件的符号特点:mangled symbol names(重整符号名称)。
在比如C这样的语言中,任何给定的名字(符号)只能对应唯一的一个函数或数据,不需要名字重整(name mangling)。即便如此,如果你看一个典型的纯C二进制的符号表,你会发现每个函数名有一个_(下划线)的前缀。例如:
$ echo 'int main() { return 0; }' | xcrun clang -x c - -o ./test $ xctest nm ./test 0000000100000000 T __mh_execute_header 0000000100000f80 T _main U dyld_stub_binder $
这种简单的“mangling”(重整)已有很长的历史,实际上没有多少用处,但仍为兼容性和一致性起到一些效果。按照惯例,C中定义的名字会有下划线,而纯汇编定义的全局符号则没有(尽管许多汇编语言的作者为了一致性,也会预先考虑下划线的定义)。
对于Objective-C语言来说,它的符号名字不会有异议或者说冲突;Objective-C的方法实现的形式是: -[class selector],并且objective - c不允许相同的类使用不同的参数来重载相同的selectors。
好吧,让我们来看一下重整名字的例子!
一个没有额外信息的简单的名字可能会产生异议,所以必须做一些处理。下面我们来看一看C++的例子:
$ cat | xcrun clang -x c++ - -o test int foo(int a) { return a * 2; } int foo(double a) { return a * 2.0; } int main() { return foo(1) + foo(1.0); } ^D $ xcrun nm -a test 0000000100000f30 T __Z3food 0000000100000f10 T __Z3fooi 0000000100000000 T __mh_execute_header 0000000100000f60 T _main U dyld_stub_binder
因为foo对应两个包含不同参数的函数,这在c++中是合法的定义,所以我们不能简单地生成两个_foo符号,因为链接器会不知道如何链接,无法区分不同的函数实现。因此,c++编译器使用一组严格的编码规则“mangles”(重整)了符号。
跟C和objective - C不一样,C++和Swift函数符号以名字本身来命名,就不足以区分一个函数的不同实现,带有不同的参数类型(比如,foo(int)和foo(双))的同名函数,需要更多信息来区分。如果使用代码中定义的全部参数信息(比如“foo(int)”),那么会在连接器中产生很多额外的代码;并且当多个类型名称映射到同样的基本类型时(如unsigned和unsigned int),也会产生混淆。所以,在c++中,使用了一些晦涩难懂的类型定义和转换规则,其结果是编译器和链接器能流畅的工作,不会有丝毫混淆。这个处理逻辑跟Swift比较类似。
上面的foo的例子的符号解释如下:
1. 首先,最前面的 '_' 表示这是C风格的符号。
2. 其次,'_Z',这个前缀标记这个符号是一个mangled(重整)的全局C++名字。
3. 接下来的数据定义了函数名字包含的字符的个数;在本例中,3foo意味着“foo”这个名字,有三个字符。
4. 字符'd'和'i'分别对应基本的'double'和'int'数据类型,返回值不属于c++函数命名的一部分,所以参数列表简单地反映函数的全名。
想获取更多的关于经典C++编译器重整名字的内容,请参考Itanium C++ ABI documentation.
总结:
Object-C类似于C语言,Swift类似于C++,有函数重载,有虚函数表,需要命名重整。
参考:
1.Swift 内部机制浅析
2.Swift Name Mangling