注释的魔力

为什么要写注释?

有个笑话是这么说的,说程序员最讨厌两件事情,一件是别人催他写注释,一件是别人的代码没写注释。

我觉得注释主要来源于以下几种情况:

  1. 记录开发者当时的思路、想法
    例如:

    //这种做法很独特,但是依然可以达到效果
    
  2. 提醒自己或者后续开发

    //TODO:这里可以替换为更快的算法
    
    
    //这里有一个Bug,但是并不会影响xx系统,修复起来代价很大,原因出在xx上面,建议不要修复此bug
    
  3. 诠释具体代码

    /** Sorts the elements of a sequence in descending order by using a specified keySelector.
    
    
     @param keySelector A selector that provides the 'key' which the array should by sorted by.
     @return An array whose elements are sorted in descending order.
     */
    - (NSArray *)linq_sortDescending:(LINQSelector)keySelector;
    
  4. 明确可能发生歧义的内容

    //计算节点总个数,每次调用都会计算一遍,没有缓存
    //名词情况下会认为直接获取,并不知道这会包含一定的计算量
    - (int)count;
    
  5. 解决疑问

    //为什么默认为10?因为10个足以。如果要加大的话,建议<=20
    int threadCount = 10;
    
  6. 老板说要加注释

    //返回 4
    return 4;
    
  7. 其他

如何写注释?

  1. 记录开发者当时的思路、想法

    这种注释一般在开发过程中可能会有一些,一般是开发者随意记录的。因为一些不可避免的原因,开发者在开发过程中会很容易被打断,而很多开发者又比较健忘(比如我),所以很多开发者会做这种小note。

    一般这种注释大都会在后续工作中逐渐删除,可能会有一部分转换为代码诠释,所以在写法上也没啥可说的。

  2. 提醒自己或者后续开发

    如果是TODO这种的,我建议直接写#warning,因为在Xcode中写TODO注释并不高亮,找起来还需要通过搜索,不是很方便。

    我记得好像有一个可以自动抽取TODO的插件,名字叫什么不记得了。

    如果是其他情况,那么建议对问题的描述更加精确一点,并让注释更加整齐一些,
    例如之前可能这么写:

    //这里有一个Bug,但是并不会影响xx系统,修复起来代价很大,原因出在xx上面,建议以后有时间修复此bug
    

    建议这么写:

    //Bug Fix
    //问题:修改xx内容导致xx异常
    //影响:暂未发现对xx系统的影响,xx系统可正常工作
    //原因:xx系统对接的yy系统在某情况下会发送...
    //修复:建议暂不修复
    
  3. 诠释具体代码

    如果是诠释方法、变量这种的,建议使用喵神的VVDocument,很棒的一款插件,基本上做iOS开发的,大部分都用这个插件写注释。

  4. 明确可能发生歧义的内容

    很多时候,这种情况主要是我们起了一个很烂的方法名或者变量名,而这可能造成巨大的危害。

    - (int)count;这个方法为例,如果一个程序员以为这并不需要计算,只是一个缓存,只需要直接获取,而频繁调用的话,那么,可能会造成很大的资源浪费。如果量级大了,可能会消耗大量时间才能计算出来。

    编写可读代码的艺术一书的,举了这么一个例子:count方法会去计算二叉树的所有节点个数,这意味着count方法会去遍历整个二叉树,但是注释没有写清楚,开发者每次想要获取个数的时候都去调用一遍。在测试的时候没有发现任何问题,这是因为量级太小,而在生产环境中,当整个二叉树的节点已经达千万级别的时候,这个问题才逐渐暴露,因为一次遍历需要耗费一个小时。

    上面这个问题都是开发者的问题吗?并不尽然,他并不知道count是极其耗时的,因为注释没写。

    这就是烂注释的危害。

    那么,这又引出了另外一个问题,是一个烂方法+一个好注释好,还是一个直观的好方法好,我觉得,还是好方法好。

    例如:

    //计算行数,计算字符串中包含的\n个数+1
    - (int)count;
    

    不如:

    //统计行数,\n个数+1
    - (int)countNewLine;
    

    对于一些复杂的方法,可能需要大量语言去描述,而通过伪代码方式却可以清晰易懂、一目了然:

    // 统计行数,\n个数+1
    // "string123" => 1
    // "string123\n" => 2
    // "string123\n123" => 2
    // "string123\n1231\n32131" => 3
    - (int)countNewLine;
    
  5. 解决疑问

    很多时候,这种注释主要是为了解决惯性思维的问题,比如,按照正常做法,我们一般会使用A方案,而在这里采用了B方案,甚至可能B方法在某些方面不如A方案,但是在这却是非常合适的,这个时候我们最好还是写一下注释。

    这种注释的好处是显而易见的,可以表明自己做出这种选择的原因,同样也可以避免后续开发者多余思考。

    对于这种注释,我建议和第二点类似,写一个Reason 或者 Hack。

    //Reason
    //原因:B方案在虽然没有A方案性能好,但这并不苛求性能,且B方案在此实现更为简单
    
  6. 老板说要加注释

    类似于这种的,与其加注释不如不加,这种注释也就只能混混统计工具,让注释占比好看点。有一些新手程序员,可能为了注释而注释,写出如下这种注释,本质上和”老板说要加注释”都是一种情况:

    // Calculate height of view
    - (int)calculateHeightOfView;
    

备注

注释是增强代码可读性的强有力工具,但是不恰当的注释也会造成极大的危害。

我建议大家阅读一下编写可读代码的艺术这本书,很薄,但是很有收获。

Article Published in on iOS

Article by 付军