ReactiveCocoa 中的@keypath宏

简单的解析一下ReactiveCocoa的@keypath宏

在xcode里面写下下面两行代码
@keypath(self.testKeyPath);
@keypath(self, testKeyPath);

在Xcode里面找到下面的选项,可以看到预编译以后的代码


ReactiveCocoa 中的@keypath宏_第1张图片
这是代码的对应关系
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"))
ReactiveCocoa 中的@keypath宏_第2张图片

这样写会导致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

你可能感兴趣的:(ReactiveCocoa 中的@keypath宏)