Date
Dec. 25th, 2024
 
2024年 11月 25日

Post: Xcode: TODO 的实现

Xcode: TODO 的实现

Published 12:07 Jul 13, 2014.

Created by @ezra. Categorized in #Programming, and tagged as #iOS.

Source format: Markdown

Table of Content

自从 Xcode Ghost 事件之后苹果就开始收缩插件功能了, 直到现在的 Xcode 已经去掉了旧形式的插件。

安全性在很大程度上算是提高了, 但是也随之带来一些不便, 比如之前不能再使用 TODO 插件等。

下面我们开始着手实现一个 TODO 效果。首先明确我们的目的, 那就是让 Xcode 产生警告, 要做到这个效果有两种途径:


Macro

TODO MACRO

TODO MACRO

相关的预处理指令有:

#warning ezra
#pragma message "ezra"
#pragma GCC warning "ezra"
#error ezra
#pragma GCC error "ezra"

但是问题来了, 我们是不能通过 #define 来使用这些预处理指令。这时候 _Pragma 就派上了用场,它可以将部分 #pragma 指令字符串化, 例如刚才提到的:

#pragma message "ezra"
// 它等价于
_Pragma("message(\"ezra\")")
// 也等价于
_Pragma("message \"ezra\"")

这下就简单了, 先来一个简单的:

#define DEFINE_WARNING _Pragma("message(\"ezra\")")
int main(void) {
    // 警告[!]ezra
    DEFINE_WARNING
    return 1;
}

警告产生了, 但我们还希望警告的内容可以自定义:

#define STRINGILY(S) #S
#define DEFER_STRINGIFY(S) STRINGIFY(S)
#define PRAGMA_MESSAGE(MSG) _Pragma(STRINGILY(message(MSG)))

其中, STRINGILY 作为辅助, 将传入的参数字符串化; 之后的 PRAGMA_MESSAGE 使用这个结果产生警告。

#define FORMATTED_MSG(T, MSG) "[" T "] " MSG
#define WARNING_MACRO(T, MSG) PRAGMA_MSG(FORMATTED_MSG(DEFER_STRINGIFY(T), STRINGIFY(MSG)))

至此, 一个基本形态已经显现。

接下来, 最后的努力, 为了让这个宏更加明显, 也更有 Objective-C 的风格, 我们要将他设计成 @TODO() 的样式:

其实本来理想的写法是类似 Swift 的样式, 即 // TODO:, 但是在这种方案中, 目前我还没有想到或到好的方案。如果你感兴趣, 请参考第二种 TODO 实现方案。如果你有好的建议, 请联系我: meniny@qq.com

#if defined(DEBUG) && !defined(NDEBUG)
#define MXKEYWORDIFY autoreleasepool {}
#else
#define MXKEYWORDIFY try {} @catch (...) {}
#endif

这个 @ 符号我们通过定义了 MXKEYWORDIFY 宏来实现。

紧接着我们最终的版本就完成了:

#define TODO(MSG) MXKEYWORDIFY WARNING_MACRO(TODO, MSG)

它的使用也很简单:

@TODO(this is a TODO macro sample)

如果你愿意你也可以改为其它样式, 比如使用 $ 开头:

#define $TODO(MSG) WARNING_MACRO(TODO, MSG)

其实 WARNING_MACRO 不单单可以用作 TODO, 你还可以对其扩展做更多的场合, 例如 FIXME 等。


Script

TODO MACRO

TODO MACRO

Build Phases 中可以增加和运行脚本大家应该都知道, 这也是我们可以利用的点之一。

点击 Build Phases 中左上角的 + 符号, 选择 Add Run Script Phase

Add Phase

展开新添加的 Run Script 模块, Shell 保持 /bin/sh, 下面的代码框中输入:

KEYWORDS="TODO:|FIXME:|TEAM:|XXX:"
find "${SRCROOT}" \( -name "*.h" -or -name "*.m" \) -print0 | xargs -0 egrep --with-filename --line-number --only-matching "($KEYWORDS).*\$" | perl -p -e "s/($KEYWORDS)/ warning: \$1/"

这段脚本中我们不仅指定了 TODO, 还指定了 FIXME、TEAM 等。因为逻辑很简单, 只是查找关键字。

使用依旧简单:

// TODO: this is a shell warning sample
Pinned Message
HOTODOGO
The Founder and CEO of Infeca Technology.
Developer, Designer, Blogger.
Big fan of Apple, Love of colour.
Feel free to contact me.
反曲点科技创始人和首席执行官。
开发、设计与写作皆为所长。
热爱苹果、钟情色彩。
随时恭候 垂询