From Brad.Cox to Chris.Lattner.
尝试利用一些业余时间研究下 Swift,写一些由 OC 到 Swift 的变化。
背景
当我使用 ClassDump 对一个项目操作的时候,输出了一些看不懂的东西。(后来知道了这是一个 OC Swift 混编的工程)
#import <UIKit/UIViewController.h>
@interface _TtC11MandrakeKit11LoadingView : UIViewController {
}
- (id)initWithCoder:(id)arg1;
- (id)initWithNibName:(id)arg1 bundle:(id)arg2;
- (void)viewDidLoad;
@end
通过 Reveal 分析 UI,我大概知道这个类是 MandrakeKit.LoadingView
,通过查看 MachO 发现 __TEXT
段中有 __swift5_typeref
,其中的数据跟这些很类似,所以这些就是Swift输出的类名。
Swift 这样进行转换的原因是为了防止不同的库不会出现命名冲突。
OC & C
OC 的符号表中没有这些复杂的符号重整,OC 使用 Selector + Type Encoding,并且OC 无重载。
C 语言有轻微的 Name Mangling,即会将 void main(){return 0;}
符号化为:_main
,增加一个下划线。
OC 举一个栗子:Point类下的 + (id) initWithX: (int) number andY: (int) number;
方法
+ (id) initWithX: (int) number andY: (int) number;
- (id) value;
_c_Point_initWithX_andY_
_i_Point_value
C++
对于如下方法,C++的NameMangling会翻译成下面这样:
int foo(int a) { return a * 2; }
int foo(double a) { return a * 2.0; }
int main() { return foo(1) + foo(1.0); }
0000000100000f30 T __Z3food
0000000100000f10 T __Z3fooi
0000000100000000 T __mh_execute_header
0000000100000f60 T _main
C++编译器遵循一套严格的 mangles 规则,参考这个链接Itanium C++ ABI documentation
以 int foo(double a)
为例,重整后的符号为 __Z3fooi
大致意思如下:
_ _Z 3 foo d
- : 代表C风格的符号
_Z : 这个前缀标记这个符号是一个mangled(重整)的全局C++名字
3 : 字符长度,foo 这个名字,有3个字符
foo :
d : 代表`double`,如果是 `int` 的话就是`i`
Swift
Swift 的重整规则基于 C++,也有些不同,包含更多的信息和概念。
栗子:
xcrun swiftc -emit-library -o test -
public class myClass{
public func calculate(x: Int) -> Int {
return 0;
}
}
nm -g test
0000000000002bb0 T _$s4test7myClassC9calculate1xS2i_tF
_$s4test7myClassC9calculate1xS2i_tF
_ // 通用起始
$s // '$s' global // Swift 稳定mangling版本
4test // 字符长度,模块名
7myClass // 方法归属的类名
C // 从属关系,myClass 是 test 模块中的 Class
9calculate // 字符 calculate
1x // 参数类型
S2i // 堆栈中放入两个 Swift.Int
_
tF // 从属关系,calculate 是 test.myClass 的 Function ???
这里解释一下S2i,这个是从后往前取的,参数往里放是栈结构,先进后出,第一个进栈的是参数 `x:Int` 第二个是返回的参数 Int,所以S2i两个`Swift.Int`。
如果入参是 `x:String`,反参是 Int,那就变成是 `SSSi` 了。
/* 此段过期,是以前的老版本
_TFC4test7MyClass9calculatefS0_FT1xSi_Si
_T // Swift通用起始标记
F // Non-curried function
C // Function of a class. (method)
4test // 字符长度,模块名
7MyClass // 方法归属的类名
9calculate // 函数名
f // 非柯里化函数(Uncurried Function)
S0 // 指定 类实例 为类型堆栈的第一个参数
_FT // 参数开始
1x // 第一个参数参数名
Si // Swift 内置类型 Swift.Int
_Si // 返回类型,同上 Swift.Int
*/
// 结果:
test.MyClass.calculate(test.MyClass) -> (x: Swift.Int) -> Swift.Int
栗子2:
didi ~ xcrun swiftc -emit-library -o test -
public func 💛 (lhs: Int, rhs: Int) -> Int {
return 0;
}
didi ~ nm -g test
0000000000002d60 T _$s4test004GrIh3lhs3rhsS2i_SitF
_ // 通用起始
$s // '$s' global // Swift 稳定mangling版本
4test // 符号 4是长度
004GrIh // 00 特殊字符 GrIh 就是 emoji U+1F49B
3lhs // 参数 3是字符串长度
3rhs // 参数 3是字符串长度
S2i // 解释参数的类型,两个Swift.Int
_ // end??? 待填坑
Si // Swift.Int
tF // 从属关系,💛 是 test 的 Function ???
//结果:
test.💛(Swift.Int, Swift.Int) -> Swift.Int
以上
对大多数人来说,不是很多。从算法的意义上讲,读取变形的名称是相当简单的,但是对于人眼来说则是不必要的困难。
这就是为什么存在拆解工具的原因。
补充
关于curried & uncurried function:
上面案例中的方法d是一个uncurried function
,因为参数是一个一个传递进去的,并不是当做一个多元元组传递进去的。(可能理解有误。。)
这个概念是跟函数式编程相关的,暂未深入了解,挖个坑,稍后填坑。
ref:stackOverFlow:How is this exactly a curried function?