Nov 29

小议:oc新增的direct方法与程序员的美学

Lrdcq , 2019/11/29 17:05 , 程序 , 閱讀(3740) , Via 本站原創
月初的时候llvm仓库出现了一个巨大的pr:[Implement __attribute__((objc_direct)), __attribute__((objc_direct_members))](https://github.com/llvm/llvm-project/commit/d4e1ba3fa9dfec2613bdcc7db0b58dea490c56b1)。简单说起来就是为oc语言添加了direct方法的功能。direct一看就是说的Direct Dispatch的,让oc像普通静态语言那样方法直接调用来提高性能(而不是Message Dispatch)。

具体描述

Direct Dispatch,其实就是就像普通的c函数那样,子程序调用链过程在编译后就直接触达了,不会在runtime里留下痕迹(应该说完全对runtime透明)。目前来看,动态语言中支持编译为Direct调用的语言几乎没有,当然如果从结果上看WebAssembly勉强算,但是似乎大部分语言并没有这样的诉求。

oc为什么提供这样的能力呢。可能更多的还是考虑,目前foundation和业界主流oc框架下,在暴露oc方法的同时基于一些考虑,还暴露了很多c方法(本身就是direct调用)。而支持direct特性可以将相关能力进行统一,降低语言学习成本吧。

按照llvm的commit上描述的信息,本次oc新增的语言特性的写法是:
@interface AClass: NSObject
@property(nonatomic) BOOL a; //普通的property
@property(nonatomic, direct) BOOL b; //direct的property

- (void)x; //普通的方法
- (void)y __attribute__((objc_direct)); //direct的方法
@end

设计上,只要在声明上动动手脚,对于的属性和方法就会编译为direct调用了。当然这里存在相当多的根本性区别:

1. direct表示的属性和方法在runtime层完全无感知了,不能通过任何runtime的方法访问/动态改变,@selector也不起效。但是在llvm编译过程中的ast中,它和其他oc元素是一致可访问的(也就是说编译前端对oc普通方法的操作同样适用于direct方法)。

2. direct标示具有唯一性,direct方法不能被非direct的重写,反之,所以基本上不能重写,当然协议也不行。

3. direct方法在img级别是私有的,毕竟在oc层不可能动态的调用到了,而它的生命形式也不是一般的c lib可以调用到了。全部木大。

4. 由于不是Message Dispatch,函数访问时的nil check是编译器单独处理的,行为特别是类方法的行为和一般的方法有些不同;_cmd的行为也有些许不同。但是这些应该不会影响普通逻辑。

目前外网上相关资料已经很多了,并且本身这个feature也不复杂,因此直接带过。
补充参考:
1. https://www.infoq.com/news/2019/11/objective-c-direct-methods/
2. https://nshipster.com/direct/

为啥加入这个feature

网上的讨论也很多,为什么苹果要在19年11月这个时间点为oc这个已经几乎官宣的语言加入这么一个类似于语法糖的功能。几个比较靠谱的分析是:

1. 虽然iOS不推荐使用oc了,但是oc这门语言苹果还是想在社区长久发展下去(oc和llvm基本可以视为是苹果的了),加入业界呼声高的feature是必要的。

2. 苹果从19年开始,逐渐开放TV,iWatch等偏向智能硬件的开发工作给开发者了,相关高性能语言能力便易程度的提升上,做一些修补。比较目前苹果系的应用开发还是摆脱不了oc,越偏系统sdk越甚。

以上提到的两种可能性,主要关键字其实并不是“必要性”,而是“便易性”。

关于程序员的“美学”

这里是另一个话题,经常会有博客或者书籍提到“程序员的美学”这个概念。直观来说就是一段代码如何优雅“美丽”,但是很少有人讨论这种感性玩意儿是如何理性的落地的。

而苹果添加direct功能这个case就是一个很好的典范。

一个经典的例子是,代码段:
//第一段
for (int i = 1; i <= 16; i *= 2) {
  call(i);
}
//第二段
call(1);
call(2);
call(4);
call(8);
call(16);

这两段代码,明显for循环更优雅知性整齐漂亮,大家都喜欢,但是第二段其实也是非常浅显易懂,并且一般来说第二段效率更高。那么灵魂拷问是,我们写工程代码,并且各方面无论是可维护性还是性能都要求极高时,应该写第一段还是第二段呢?

其实如果真的要追求完美,代码上,肯定选择第一种,但是如果我们当前选择的技术栈——比如这门语言,无法让第一种写法和第二种写法效率一致,那我们需要1. 让当前语言/编译器支持相关优化能力。2. 更换技术栈。

当前oc遇到的也是这么一个问题:
@interface AClass: NSObject
- (void)x; //普通的方法
@end
extern void y(AClass *self);//需要高性能的方法

部分方法通过c函数的方式暴露出来,不够优雅,可维护性差,直接写成普通oc方法有瓶颈:于是通过添加direct的语言特性解决这个问题。至少不至于让用户遇到这个问题需要选择:把整个类降级为c/cpp;或者改用别的语言(如果真有得选)。

所以就事论事而言,程序员追求的美学,就是通过特性/工具/方法让事情可以高效/永久性的做到人人满意以至于完美的过程。想必这也是大家从心底认可的东西吧。
关键词:objective-c , direct
logo