RAC宏

作者: boy丿log | 来源:发表于2019-04-03 16:06 被阅读0次

    一、基础宏

    1.metamacro_stringify

    #define metamacro_stringify(VALUE) \
            metamacro_stringify_(VALUE)
     
    #define metamacro_stringify_(VALUE) # VALUE
    

    这样写的目的是预防参数中传入宏定以后,以宏定义的名字做为参数

    #define number 10
    #define add(a,b) add_(a,b) //先取得宏的值
    
    #define add_(a,b) (a ## 101 ## b)//拼接
    

    2.metamacro_concat(A, B)

    #define metamacro_concat(A, B) \
            metamacro_concat_(A, B)
    #define metamacro_concat_(A, B) A ## B
    

    拼接两个参数

    3.metamacro_argcount(...) 和 metamacro_at(N, ...)

    #define metamacro_argcount(...) \
            metamacro_at(20, __VA_ARGS__, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1)
            
    #define metamacro_at20(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, ...) metamacro_head(__VA_ARGS__)
    
    #define metamacro_head(...) \
            metamacro_head_(__VA_ARGS__, 0)
    #define metamacro_head_(FIRST, ...) FIRST
    
    #define metamacro_head(FIRST,..., 0)  FIRST
    

    第一个是获取参数数量,第二是获取参数的第一个参数,
    metamacro_argcount 变参传入后,通过在后面拼接20个参数,通过metamacro_head_宏来获取20位置的数值,此为参数个数

    设计灵感来自P99社库

    4.metamacro_foreach(MACRO, SEP, ...) 和 metamacro_foreach_cxt(MACRO, SEP, CONTEXT, ...)

    #define metamacro_foreach_cxt(MACRO, SEP, CONTEXT, ...) \
            metamacro_concat(metamacro_foreach_cxt, metamacro_argcount(__VA_ARGS__))(MACRO, SEP, CONTEXT, __VA_ARGS__)
            
    展开后等价于
    metamacro_foreach_cxtN(MACRO, SEP, CONTEXT, __VA_ARGS__)
    
    #define metamacro_foreach_cxt20(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19) \
        metamacro_foreach_cxt19(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18) \
        SEP \
        MACRO(19, CONTEXT, _19)
        
    
    

    这个宏是为了批量处理一些宏定义,中间加上一个参数

    Sep
    MACRO(19, CONTEXT, _19) 传入参数,宏,宏之间的间隔(SEP)
    ,还有变参,来进行批量操作

    define metamacro_foreach(MACRO, SEP, ...) \
            metamacro_foreach_cxt(metamacro_foreach_iter, SEP, MACRO, __VA_ARGS__)
            
     #define weakify(...) \
        rac_keywordify \
        metamacro_foreach_cxt(rac_weakify_,, __weak, __VA_ARGS__)   
        
        #define strongify(...) \
        rac_keywordify \
        _Pragma("clang diagnostic push") \
        _Pragma("clang diagnostic ignored \"-Wshadow\"") \
        metamacro_foreach(rac_strongify_,, __VA_ARGS__) \
        _Pragma("clang diagnostic pop")   
    
    

    MACRO(19, _19) 传入参数,宏,宏之间的间隔(SEP)
    ,还有变参,来进行批量操作,少传入了context的参数

    5.metamacro_foreach_cxt_recursive(MACRO, SEP, CONTEXT, ...)

          #define metamacro_foreach_cxt_recursive20(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19) \
        metamacro_foreach_cxt_recursive19(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18) \
        SEP \
        MACRO(19, CONTEXT, _19)
    

    跟metamacro_foreach_cxt(MACRO, SEP, CONTEXT, ...)一样

    由于宏在递归展开中可能会导致递归前置条件失败,在这种情况下,应该使用这个递归宏。当然,它的效果和metamacro_foreach_cxt(MACRO, SEP, CONTEXT, ...)宏是完全一样的。

    6.metamacro_foreach_concat(BASE, SEP, ...)

        BASE_0 \
        SEP \
        BASE_1 \
        SEP \
        BASE_2 \
        SEP \
        BASE_3 \
         ……
         ……
         ……
         ……
         ……
         ……
     
        SEP \
        BASE_N - 4 \
        SEP \
        BASE_N - 3 \
        SEP \
    

    metamacro_foreach_concat(BASE, SEP, ...)宏如同它的名字一样,把可变参数里面每个参数都拼接到BASE后面,每个参数拼接完成之间都用SEP分隔。

    试想一种场景:

    如果有一连串的方法,方法名都有一个相同的前缀,后面是不同的。这种场景下,利用metamacro_foreach_concat(BASE, SEP, ...)宏是非常爽的,它会一口气组合出相关的一列表的不同的宏

    7.metamacro_for_cxt(COUNT, MACRO, SEP, CONTEXT)

    #define metamacro_for_cxt(COUNT, MACRO, SEP, CONTEXT) \
            metamacro_concat(metamacro_for_cxt, COUNT)(MACRO, SEP, CONTEXT)
    
    metamacro_for_cxtN(MACRO, SEP, CONTEXT)
    
    
    #define metamacro_for_cxtN(MACRO, SEP, CONTEXT) \
            metamacro_for_cxtN - 1(MACRO, SEP, CONTEXT) \
            SEP \
            MACRO(N - 1, CONTEXT)
    

    这个宏的用途是执行COUNT次MACRO宏命令,每次MACRO宏命令的第一个参数都会从COUNT开始递减到0。

    8.metamacro_tail(...) 的作用就是取出可变参数列表除去第一个参数以外的所有参数。(至少两个参数)

    9.etamacro_head(...) 的作用就是取出可变参数列表的第一个参数。(至少一个参数)

    10.metamacro_take(N, ...)

    #define metamacro_take20(...) metamacro_head(__VA_ARGS__), metamacro_take19(metamacro_tail(__VA_ARGS__))
    

    取出变参前n个元素

    11.metamacro_drop(N, ...)

    #define metamacro_drop20(...) metamacro_drop19(metamacro_tail(__VA_ARGS__))
    

    取出变参一个数量后的参数

    12. metamacro_dec(VAL) 和 metamacro_inc(VAL)

    第一个取得前一位数,后面的取得后一位数

    13. metamacro_if_eq(A, B)

         NSInteger a =  metamacro_if_eq(3, 3)(({NSLog(@"1");3;}))(({NSLog(@"1231312");4;}));
         NSLog(@"%ld", a);
    

    比较两个数,然后确定哪个方法

    14.metamacro_if_eq_recursive(A, B)

    同上

    15.metamacro_is_even(N)

    N的值域在[0,20]之间。
    取偶

    #define metamacro_is_even(N) \
            metamacro_at(N, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1)
            
    

    16. metamacro_not(B)

    只能取1

    #define metamacro_not(B) \
            metamacro_at(B, 1, 0)
    

    二、 常用宏

    1.RACTuplePack_class_name

    跟metamacro_argcount类似,取得RACTuple的类名
    RACTuplePack_object_or_ractuplenil 根据传入参数是否为nil,转换为RACTuple

    2.RACObserve(TARGET, KEYPATH)

    
    #define RACObserve(TARGET, KEYPATH) \
        ({ \
            _Pragma("clang diagnostic push") \
            _Pragma("clang diagnostic ignored \"-Wreceiver-is-weak\"") \
            __weak id target_ = (TARGET); \
            [target_ rac_valuesForKeyPath:@keypath(TARGET, KEYPATH) observer:self]; \
            _Pragma("clang diagnostic pop") \
        })
        
        
    #define keypath(...) \
        metamacro_if_eq(1, metamacro_argcount(__VA_ARGS__))(keypath1(__VA_ARGS__))(keypath2(__VA_ARGS__))
     
    #define keypath1(PATH) \
        (((void)(NO && ((void)PATH, NO)), strchr(# PATH, '.') + 1))
     
    #define keypath2(OBJ, PATH) \
        (((void)(NO && ((void)OBJ.PATH, NO)), # PATH))
    
    1. 加void是为了不会出现警告
    2. 加NO是C语言判断条件短路表达式。增加NO && 以后,预编译的时候看见了NO,就会很快的跳过判断条件。
    3. strchr函数原型如下:
     extern char *strchr(const char *s,char c);
    
    查找字符串s中首次出现字符c的位置。返回首次出现字符c的位置的指针,返回的地址是被查找   字符串指针开始的第一个与字符c相同字符的指针,如果字符串中不存在字符c则返回NULL。,返回首次出现字符后的字符串
    
    1. 当输入self.的时候,会出现编译器的语法提示,原因是OBJ.PATH,因为这里的点,所以输入第二个参数时编辑器会给出正确的代码提示。
    2. 使用keypath(...)的时候前面会加上@符号,原因是经过keypath1(PATH)和keypath2(OBJ, PATH)之后出现的结果是一个C的字符串,前面加上@以后,就变成了OC的字符串了。
    3. ((void)(NO && ((void)OBJ.PATH, NO))是为了编译器可以出现语法提示,没有其他作用

    用法:

    // 例子1,一个参数的情况,会调用keypath1(PATH)
    NSString *UTF8StringPath = @keypath(str.lowercaseString.UTF8String);
    // 输出=> @"lowercaseString.UTF8String"
     
     
    // 例子2,2个参数的情况,支持自省
    NSString *versionPath = @keypath(NSObject, version);
    //  输出=> @"version"
     
    // 例子3,2个参数的情况
    NSString *lowercaseStringPath = @keypath(NSString.new, lowercaseString);
    // 输出=> @"lowercaseString"
    

    集合类型的

    #define collectionKeypath(...) \
        metamacro_if_eq(3, metamacro_argcount(__VA_ARGS__))(collectionKeypath3(__VA_ARGS__))(collectionKeypath4(__VA_ARGS__))
     
    #define collectionKeypath3(PATH, COLLECTION_OBJECT, COLLECTION_PATH) ([[NSString stringWithFormat:@"%s.%s",keypath(PATH), keypath(COLLECTION_OBJECT, COLLECTION_PATH)] UTF8String])
     
    #define collectionKeypath4(OBJ, PATH, COLLECTION_OBJECT, COLLECTION_PATH) ([[NSString stringWithFormat:@"%s.%s",keypath(OBJ, PATH), keypath(COLLECTION_OBJECT, COLLECTION_PATH)] UTF8String]
    

    3. RAC(TARGET, ...)

    #define RAC(TARGET, ...) \
            metamacro_if_eq(1, metamacro_argcount(__VA_ARGS__)) \
            (RAC_(TARGET, __VA_ARGS__, nil)) \
            (RAC_(TARGET, __VA_ARGS__))
    
    #define RAC_(TARGET, KEYPATH, NILVALUE) \
            [[RACSubscriptingAssignmentTrampoline alloc] initWithTarget:(TARGET) nilValue:(NILVALUE)][@keypath(TARGET, KEYPATH)]
            
    

    我们都知道RAC(TARGET, ...)宏是用来把一个信号绑定给一个对象的属性,绑定之后,每次信号发送出一个新的值,就会自动设定到执行的keypath中。当信号完成之后,这次绑定也会自动的解除。
    RAC_(TARGET, KEYPATH, NILVALUE) 会把信号绑定到TARGET指定的KEYPATH上。如果信号发送了nil的值,那么会替换成NILVALUE赋值给对应的属性值上。

    RAC_(TARGET, VA_ARGS)只不过是RAC_(TARGET, KEYPATH, NILVALUE)第三个参数为nil。

    4. onExit

    #define onExit \
        rac_keywordify \
        __strong rac_cleanupBlock_t metamacro_concat(rac_exitBlock_, __LINE__) __attribute__((cleanup(rac_executeCleanupBlock), unused)) = ^
    

    @onExit定义当前代码段退出时要执行的一些代码。代码必须用大括号括起来并以分号结尾,无论是何种情况(包括出现异常,goto语句,return语句,break语句,continue语句)下跳出代码段,都会执行onExit后面的代码。

    @onExit提供的代码被放进一个block块中,之后才会执行。因为在闭包中,所以它也必须遵循内存管理方面的规则。@onExit是以一种合理的方式提前退出清理块。

    在相同代码段中如果有多个@onExit语句,那么他们是按照反字典序的顺序执行的。

    @onExit语句不能在没有大括号的范围内使用。在实际使用过程中,这不是一个问题,因为@onExit后面如果没有大括号,那么它是一个无用的结构,不会有任何事情发生。

    相关文章

      网友评论

          本文标题:RAC宏

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