1. 问题

在 AppHost.framework【注1】(以下简称 AppHost) 的编码中,需要处理很多预先导入到 webview 里的 js 文件,有一些不关键功能是用 .js 的文件读到内存的,还有一些比较短小的 js 源码,如;

1
2
3
4
          (function(e){
                e.setAttribute('src','%@');
                document.getElementsByTagName('body')[0].appendChild(e);
            })(document.createElement('script'));

要写到代码里和 objc 代码一起。还有,在 AppHost 里,有个 ah_doc 模块,在编写注释时,需要输出完整的 js 代码,如

1
2
3
4
5
6
7
8
window.appHost.invoke('startNewPage', { 'url': 'http://you.163.com/','title': 'title',
    'type': "push",
    'backPageParameter': {
        'url': 'http://qian.163.com',
        'title': 'title',
        'type': 'push'
    }
})

这个字符串里包含了大量的单引号、双引号,而且为了保持可读性、维护性,需要多行输出。

这是非常典型的问题,传统的多行字符串声明有没有方法很好的解决?

2.常见多行声明方式

  1. 利用@““包裹起来,换行
1
2
3
4
NSString *a = @"[AppHost] "
    @"It is called \"jsbridge\"."
    @"like window.ah('ready') = function(){};"
    @"fail to inject javascript";
  1. 利用"“包裹起来,换行
1
2
3
4
NSString *b = @"[AppHost] "
    "It is called \"jsbridge\"."
    "like window.ah('ready') = function(){};"
    "fail to inject javascript";
  1. 最最常见的 \ 号分隔,保留缩进(注意观察截图里的 linenum 和代码的距离分隔符,保留缩进

  2. 最最常见的 \ 号分隔,不保留缩进(注意观察截图里的 linenum 和代码的距离分隔符,不保留缩进

  3. 非多行

1
NSString *d = @"[AppHost] It is called \"jsbridge\".like window.ah('ready') = function(){};fail to inject javascript";

输出结果 结果

可见,对于 a\b\c1\d 变量的值而言等价的。对 c,有个陷阱:实际上获得的字符串是包含编辑器缩进。我们现在基本都会让编辑器自动缩进代码,所以容易出错。

上面 4 种写法,都需要处理字符" 引号嵌套的问题,或者需要手动加 " 号,或手动写 \ 号,要么一行损失掉可读性。 作者一般用 d 方法:在 vscode 里写好 JavaScript 代码,放在 sublime text 里 先replace("\"","\\\""),然后command+j 合并行,再贴到Xcode 里。直到我发现无意中创建的一个宏完美的解决了这个问题。

1
2
3
4
5
6
7
8
ah_doc_code(window.appHost.invoke('startNewPage', { 'url': 'http://you.163.com/','title': 'title',
    'type': "push",
    'backPageParameter': {
        'url': 'http://qian.163.com',
        'title': 'title',
        'type': 'push'
    }
}))

####优点

  1. 多行,保存源码中的缩进和换行
  2. 单双引号混排,不需要转义
  3. 使用简单,只需要包含在对()中,甚至都不需要外层加双引号、#号(NSString)
  4. 神似 ruby 中的多行字符串声明(以下代码是 AppHost 里生成自动测试用例的片段)ruby 中的多行字符串

3. 那个宏 lei 了

1
#define ah_ml(str) @#str

🍄简单的令人发指。用例:

1
2
3
4
result = [NSString stringWithFormat:ah_ml((function(e){
                e.setAttribute("src",'%@');
                document.getElementsByTagName('body')[0].appendChild(e);
            })(document.createElement('script'));), urlToRequest.absoluteString];

查看预处理的代码,代码e.setAttribute("src",'%@');里的引号自动转义。

1
result = [NSString stringWithFormat:@"(function(e){ e.setAttribute(\"src\",'%@'); document.getElementsByTagName('body')[0].appendChild(e); })(document.createElement('script'));", urlToRequest.absoluteString];

完美呈现。

4.注

  1. AppHost.framework 是作者在网易有钱、严选工作中,抽离出来 JSBridge 的库,实现 native 和 h5 之间的通讯,内置诸多常用的功能,在业务简单的情况下可开箱即用,复杂情况允许灵活定制。预计在4月底开源。