简单的解析一下ReactiveCocoa的@keypath宏
在xcode里面写下下面两行代码
@keypath(self.testKeyPath);
@keypath(self, testKeyPath);
在Xcode里面找到下面的选项,可以看到预编译以后的代码
这是代码的对应关系
1.
@keypath(self.testKeyPath);
@(((void)(__objc_no && ((void)self.testKeyPath, __objc_no)), strchr("self.testKeyPath", '.') + 1));
2.
@keypath(self, testKeyPath);
@(((void)(__objc_no && ((void)self.testKeyPath, __objc_no)), "testKeyPath"));
我们从外到内一层一层的看第一个表达式
1>最外层是@(),负责把c语言字符串转为NSString类型,最后整个表达式等价于@("testKeyPath")
2>去掉最外层,得到的是
((void)(__objc_no && ((void)self.testKeyPath, __objc_no)),
strchr("self.testKeyPath", '.') + 1)
这是一个逗号表达式,左边用来校验表达式是否合法,如果不合法,编写代码的时候编辑器会给出提示
而右边的表达式是最终获得的c语言字符串,
strchr("self.testKeyPath", '.') + 1)
strchr("self.testKeyPath", '.')返回的是".testKeyPath"的指针,加1以后就拿到了”testKeyPath"
接下来看下左边的表达式是怎么校验表达式合法性的。
(void)(__objc_no && ((void)self.testKeyPath, __objc_no))
首先普及一条规则,逗号表达式左边的值加上void以后,逗号表达式会忽略左边的值。
我们从简单到复杂写这个宏,看会发生什么
首先,最简单的,我们可以写成
@(((void)self.testKeyPath, "testKeyPath"))
这样写会导致testKeyPath方法被调用一次,这无疑意味着会增加性能损耗,这并不是我们想要的
所以可以继续扩展一下,写成
@(((void)(NO&&self.testKeyPath), "testKeyPath"))
用短路与,防止self.testKeyPath被执行,然而还是编译不过,NO的类型和self.testKeyPath不匹配
这时候我们需要&&后面的表达式也返回一个BOOL类型。
藉由前面的经验,可以写成((void)self.testKeyPath, NO),用void忽略逗号左边的值,此时表达式的值为NO是一个BOOL类型。
于是,整个表达式变成了
@(((void)(NO&&((void)self.testKeyPath, NO)), "testKeyPath"))
和最开始的表达式完全一致。
ReactiveCocoa使用如此巧妙的方式实现了@keypath宏,让它既可以拥有代码提示,又可以在编译时校验表达式的合法性,让我们不至于写出错误的keyPath