美文网首页
Objective-C 安全的 keypath

Objective-C 安全的 keypath

作者: rickytan | 来源:发表于2017-11-29 15:20 被阅读21次

    KVOKVCObjective-C 语言非常强大的两个特性,从一开始的似懂非懂到慢慢了解它的底层实现,才感受到这门动态语言的魅力所在。
    KVC 允许通过一个点分隔的字符串来设置一个对象的属性值,而 KVO 可以通过点分隔的字符串来监听对象属性值的改变。

    @interface Foo : NSObject
    @property (nonatomic) NSString *name;
    @property (nonatomic) NSInteger age;
    @end
    
    @interface Bar : NSObject
    @property (nonatomic) Foo *foo;
    @end
    
    Bar *bar = [Bar new];
    [bar addObserver:self
          forKeyPath:@"foo.name"
             options:NSKeyValueObservingOptionNew
             context:NULL];
    [bar setValue:@20 forKeyPath:@"foo.age"];    // 当然这个是无效的,因为 foo 为 nil
    

    既然是字符串,人就可能犯错,因为它无法自动补全。比如常见的拼写错误,或者一个不存在的 keypath。如何能让 Xcode 在编译时就检查出这些错误呢?

    libextobjc 的启发,本人作了一个宏来处理此事,编译时检查错误,且能自动补全。

    Gist

    #define __mz_macro_concat(A, B)     __mz_macro_concat_(A, B)
    #define __mz_macro_argcount(...)    __mz_macro_at(6, __VA_ARGS__, 6, 5, 4, 3, 2, 1)
    #define __mz_macro_head(...)        __mz_macro_head_(__VA_ARGS__, 0)
    #define __mz_macro_at(N, ...)       __mz_macro_concat(__mz_macro_at, N)(__VA_ARGS__)
    
    #define __mz_macro_at0(...) __mz_macro_head(__VA_ARGS__)
    #define __mz_macro_at1(_0, ...) __mz_macro_head(__VA_ARGS__)
    #define __mz_macro_at2(_0, _1, ...) __mz_macro_head(__VA_ARGS__)
    #define __mz_macro_at3(_0, _1, _2, ...) __mz_macro_head(__VA_ARGS__)
    #define __mz_macro_at4(_0, _1, _2, _3, ...) __mz_macro_head(__VA_ARGS__)
    #define __mz_macro_at5(_0, _1, _2, _3, _4, ...) __mz_macro_head(__VA_ARGS__)
    #define __mz_macro_at6(_0, _1, _2, _3, _4, _5, ...) __mz_macro_head(__VA_ARGS__)
    
    #define MZKVOKeyPath(Class, ...)    __mz_macro_concat(MZKVOKeyPath, __mz_macro_argcount(__VA_ARGS__))(Class, __VA_ARGS__)
    
    #define MZKVOKeyPath0(Class) ((Class *)nil)
    #define MZKVOKeyPath1(Class, path0) ((void)(NO && ((void)(((Class *)nil).path0), NO)), @#path0)
    #define MZKVOKeyPath2(Class, path0, path1) ((void)(NO && ((void)((((Class *)nil).path0).path1), NO)), @#path0 "." #path1)
    #define MZKVOKeyPath3(Class, path0, path1, path2) ((void)(NO && ((void)(((((Class *)nil).path0).path1).path2), NO)), @#path0 "." #path1 "." #path2)
    #define MZKVOKeyPath4(Class, path0, path1, path2, path3) ((void)(NO && ((void)((((((Class *)nil).path0).path1).path2).path3), NO)), @#path0 "." #path1 "." #path2 "." #path3)
    #define MZKVOKeyPath5(Class, path0, path1, path2, path3, path4) ((void)(NO && ((void)(((((((Class *)nil).path0).path1).path2).path3).path4), NO)), @#path0 "." #path1 "." #path2 "." #path3 "." #path4)
    #define MZKVOKeyPath6(Class, path0, path1, path2, path3, path4, path5) ((void)(NO && ((void)((((((((Class *)nil).path0).path1).path2).path3).path4).path5), NO)), @#path0 "." #path1 "." #path2 "." #path3 "." #path4 "." #path5)
    
    #define __mz_macro_concat_(A, B) A ## B
    #define __mz_macro_head_(FIRST, ...) FIRST
    

    使用方法

    MZKVOKeyPath 宏中,第一个参数传一个 Class 类型,后面跟它的实例的属性,就像使用 . 点操作一样,可以一级一级往下。

    [bar addObserver:self
          forKeyPath:MZKVOKeyPath(Bar, foo, name)  // 打了 Bar 之后,Xcode 会自动补全
             options:NSKeyValueObservingOptionNew
             context:NULL];
    

    注意:最多支持到六层 keypath,需要更多您可以自己修改。

    相关文章

      网友评论

          本文标题:Objective-C 安全的 keypath

          本文链接:https://www.haomeiwen.com/subject/mnoubxtx.html