ReactiveCocoa 学习笔记二十四(ReactiveCocoa 中的宏)

ReactiveCocoa 中的宏

RACAnnotations

#ifndef RAC_WARN_UNUSED_RESULT
#define RAC_WARN_UNUSED_RESULT __attribute__((warn_unused_result))
#endif

RAC_WARN_UNUSED_RESULT 是在 ReactiveCocoa 框架内部声明方法时使用的宏,其旨在提示方法执行后返回的值未使用。

RACmetamacros

  1. 执行表达式并始终返回真

    #define metamacro_exprify(...) ((__VA_ARGS__), true)
    

    可变数量的参数都会执行,但是该宏最终都是返回一个 true 值。

  2. 字符串化

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

    # + VALUE 的形式会在 VALUE 前后添加双引号,使其成为字符串,而若要得到 NSString 对象,那么可以在该宏前面添加 @ 符号。

  3. 拼接

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

    A + ## + B 的形式会将 AB 展开后再拼接为一个值。

  4. 返回第一个参数

    #define metamacro_head(...) metamacro_head_(__VA_ARGS__, 0)
    
    #define metamacro_head_(FIRST, ...) FIRST
    

    该宏返回可变参数的第一个参数,如果没有提供参数,那么返回 0

  5. 返回指定位置的参数

    #define metamacro_at0(...) metamacro_head(__VA_ARGS__)
    #define metamacro_at1(_0, ...) metamacro_head(__VA_ARGS__)
    #define metamacro_at2(_0, _1, ...) metamacro_head(__VA_ARGS__)
    
    ...
    
    #define metamacro_at19(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, ...) metamacro_head(__VA_ARGS__)
    #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__)
    

    metamacro_at + N 的形式表示取参数中的第 N + 1 个参数,所以在使用该系列宏时,所传递的参数个数应当至少是 N + 1 个。

    另外,应注意这里宏的声明中的 _0, _1, _2, ... , _19 都只是形参,起到调整 metamacro_head(__VA_ARGS__) 中参数个数的作用。

    当然,一般不会直接使用上面的宏,而是使用下面的宏

    #define metamacro_at(N, ...) metamacro_concat(metamacro_at, N)(__VA_ARGS__)

    使用该宏时,可变参数个数应当至少为 N + 1 个,其展开过程如下:

    N = 3, ... = a,b,c,d,e,f,g,h ,那么展开过程如下

    metamacro_at(3,a,b,c,d,e,f,g,h) =>
    metamacro_concat(metamacro_at, 3)(a,b,c,d,e,f,g,h) =>
    metamacro_at3(a,b,c,d,e,f,g,h) =>
    metamacro_head(d,e,f,g,h) =>
    d

    所以,最后返回 d

    N 的值应当在 0~20 的范围内,包括 0 和 20 。另外,参数的索引是从 0 开始计数的。

  6. 返回参数个数

    #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)
    

    该宏运用了上面的 metamacro_at 宏,进一步则是运用了 metamacro_at20 宏,从其定义可知,它的前 20 个参数实际上是无效的,最后返回的是剩余参数的第一个值。

    而返回的值恰好就是 __VA_ARGS__ 可变参数的个数,当然,这个个数最小为 1 ,最大为 20 ,所以使用该宏计算参数个数时,要保证参数个数在 1~20 之间,包括 1 和 20 。

  7. 遍历参数并将其传递给宏执行

    #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)
        
    #define 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) \
        metamacro_foreach_cxt18(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17) \
        SEP \
        MACRO(18, CONTEXT, _18)
    
    ...
    
    #define metamacro_foreach_cxt2(MACRO, SEP, CONTEXT, _0, _1) \
        metamacro_foreach_cxt1(MACRO, SEP, CONTEXT, _0) \
        SEP \
        MACRO(1, CONTEXT, _1)
    
    #define metamacro_foreach_cxt1(MACRO, SEP, CONTEXT, _0) MACRO(0, CONTEXT, _0)
    
    #define metamacro_foreach_cxt0(MACRO, SEP, CONTEXT)
    

    上面一系列的宏,实际是层层嵌套的,如 metamacro_foreach_cxt20 展开后包含 metamacro_foreach_cxt19 + 分隔 SEP + MACRO(19, CONTEXT, _19) ,而 metamacro_foreach_cxt19 继续展开为 metamacro_foreach_cxt18 + 分隔 SEP + MACRO(18, CONTEXT, _18) 以此类推,直到 metamacro_foreach_cxt1(MACRO, SEP, CONTEXT, _0) 展开为 MACRO(0, CONTEXT, _0) 为止。所以,该系列宏最终展开类似下面的形式:

    MACRO(0, CONTEXT, _0) SEP MACRO(1, CONTEXT, _1) SEP MACRO(2, CONTEXT, _2) SEP ... SEP MACRO(19, CONTEXT, _19)
    

    当然,具体展开要看使用的是哪一个宏,并且 metamacro_foreach_cxt0(MACRO, SEP, CONTEXT) 表示没有参数需要传递给 MACRO 进行执行,而且这里也只支持 20 个参数。

    同样,通常不会直接使用上面的宏,而是使用下面两个宏

    #define metamacro_foreach(MACRO, SEP, ...) \
            metamacro_foreach_cxt(metamacro_foreach_iter, SEP, MACRO, __VA_ARGS__)
            
    #define metamacro_foreach_cxt(MACRO, SEP, CONTEXT, ...) \
            metamacro_concat(metamacro_foreach_cxt, metamacro_argcount(__VA_ARGS__))(MACRO, SEP, CONTEXT, __VA_ARGS__)        
    
    • metamacro_foreach_cxt 宏展开过程中,其会计算可以遍历的参数个数,从而展开为对应的宏。

    • metamacro_foreachmetamacro_foreach_cxt 类似,但是其并不会提供 CONTEXT 参数给最终的宏执行,该实现是通过下面的宏:

      #define metamacro_foreach_iter(INDEX, MACRO, ARG) MACRO(INDEX, ARG)
      

      可见,MACRO 只接收两个参数,一个是参数索引,另一个是参数值。

  8. 遍历参数并将其传递给宏执行

    #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)
    
    #define 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) \
        metamacro_foreach_cxt_recursive18(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17) \
        SEP \
        MACRO(18, CONTEXT, _18)
    
    ...
    
    #define metamacro_foreach_cxt_recursive2(MACRO, SEP, CONTEXT, _0, _1) \
        metamacro_foreach_cxt_recursive1(MACRO, SEP, CONTEXT, _0) \
        SEP \
        MACRO(1, CONTEXT, _1)
    
    #define metamacro_foreach_cxt_recursive1(MACRO, SEP, CONTEXT, _0) MACRO(0, CONTEXT, _0)
    
    #define metamacro_foreach_cxt_recursive0(MACRO, SEP, CONTEXT)
    

    metamacro_foreach_cxt_recursive 系列宏同 metamacro_foreach_cxt 系列宏类似,都是将指定的参数传递给指定的宏,多个参数的处理结果相拼接,从而返回最终的结果。

    同样的,该系列宏不会直接使用,而是通过下面的宏进行使用:

    #define metamacro_foreach_cxt_recursive(MACRO, SEP, CONTEXT, ...) \
            metamacro_concat(metamacro_foreach_cxt_recursive, metamacro_argcount(__VA_ARGS__))(MACRO, SEP, CONTEXT, __VA_ARGS__)
    

    这个宏同 metamacro_foreach_cxt 等同,只是如果前者若因为宏的递归扩展而出错,那么可以使用 metamacro_foreach_cxt_recursive 宏进行规避。

  9. 拼接参数

    #define metamacro_foreach_concat(BASE, SEP, ...) \
            metamacro_foreach_cxt(metamacro_foreach_concat_iter, SEP, BASE, __VA_ARGS__)
    
    #define metamacro_foreach_concat_iter(INDEX, BASE, ARG) metamacro_foreach_concat_iter_(BASE, ARG)
    
    #define metamacro_foreach_concat_iter_(BASE, ARG) BASE ## ARG
    

    使用 metamacro_foreach_concat 宏,展开后的形式与下面的格式类似:

    BASE ## ARG1 SEP BASE ## ARG2 SEP ... SEP BASE ## ARG18 SEP BASE ## ARG19
    

    具体最后返回什么取决于参数的个数和内容。

  10. 执行指定次数的宏并拼接所有的结果

    #define metamacro_for_cxt20(MACRO, SEP, CONTEXT) \
        metamacro_for_cxt19(MACRO, SEP, CONTEXT) \
        SEP \
        MACRO(19, CONTEXT)
        
    #define metamacro_for_cxt19(MACRO, SEP, CONTEXT) \
        metamacro_for_cxt18(MACRO, SEP, CONTEXT) \
        SEP \
        MACRO(18, CONTEXT)
    
    ...
    
    #define metamacro_for_cxt1(MACRO, SEP, CONTEXT) MACRO(0, CONTEXT)
    
    #define metamacro_for_cxt0(MACRO, SEP, CONTEXT)
    

    metamacro_for_cxt 系列宏同 metamacro_foreach_cxt 系列宏类似,但是其并不会传递参数给要使用的宏。

    该系列宏不会直接使用,而是通过下面的宏进行使用。

    #define metamacro_for_cxt(COUNT, MACRO, SEP, CONTEXT) \
            metamacro_concat(metamacro_for_cxt, COUNT)(MACRO, SEP, CONTEXT)
    
    • metamacro_for_cxt 宏直接指定了执行指定宏的次数,每次执行只会传递次数索引和上下文,而没有具体的参数。
  11. 获取参数中除了第一个参数的剩余参数

    #define metamacro_tail(...) metamacro_tail_(__VA_ARGS__)
            
    #define metamacro_tail_(FIRST, ...) __VA_ARGS__
    
  12. 获取指定个数个参数

    #define metamacro_take20(...) metamacro_head(__VA_ARGS__), metamacro_take19(metamacro_tail(__VA_ARGS__))
    
    #define metamacro_take19(...) metamacro_head(__VA_ARGS__), metamacro_take18(metamacro_tail(__VA_ARGS__))
    
    ...
    
    #define metamacro_take1(...) metamacro_head(__VA_ARGS__)
    
    #define metamacro_take0(...)
    

    获取指定个数个参数,返回的是一个以 , 分隔的参数列表,所以如果想直接使用,可以将其赋值给数组对象,如下:

    id test = @[metamacro_take2(self,self.view)];
    

    但是,一般不直接使用上面的宏,而是使用下面的宏。

    #define metamacro_take(N, ...) metamacro_concat(metamacro_take, N)(__VA_ARGS__)
    

    如下:

    id test = @[metamacro_take(2,self, self.view)];
    
  13. 移除指定个数个参数

    同上面获取前 N 个参数类似,该宏可以移除前 N 个参数,返回剩余参数的列表。

    #define metamacro_drop20(...) metamacro_drop19(metamacro_tail(__VA_ARGS__))
    
    #define metamacro_drop19(...) metamacro_drop18(metamacro_tail(__VA_ARGS__))
    
    ...
    
    #define metamacro_drop1(...) metamacro_tail(__VA_ARGS__)
    
    #define metamacro_drop0(...) __VA_ARGS__
    

    同样,不必直接使用上面的宏,而是使用下面的宏。

    #define metamacro_drop(N, ...) metamacro_concat(metamacro_drop, N)(__VA_ARGS__)
    

    使用时,提供的参数个数应当不少于 N

  14. 减一操作

    #define metamacro_dec(VAL) \
            metamacro_at(VAL, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19)
    

    VAL 的值进行减一操作,所以这里 VAL 必须是数字,并且范围必须在 0~20 内,包括 0 和 20 。

  15. 增一操作

    #define metamacro_inc(VAL) \
            metamacro_at(VAL, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21)
    

    VAL 的值进行增一操作,所以这里 VAL 必须是数字,并且范围必须在 0~20 内,包括 0 和 20 。

  16. 判断值是否相等

    #define metamacro_if_eq20(VALUE) metamacro_if_eq19(metamacro_dec(VALUE))
    
    #define metamacro_if_eq19(VALUE) metamacro_if_eq18(metamacro_dec(VALUE))
    
    ...
    
    #define metamacro_if_eq2(VALUE) metamacro_if_eq1(metamacro_dec(VALUE))
    
    #define metamacro_if_eq1(VALUE) metamacro_if_eq0(metamacro_dec(VALUE))
    
    #define metamacro_if_eq0(VALUE) metamacro_concat(metamacro_if_eq0_, VALUE)
    

    metamacro_if_eq 系列宏在使用时,层层展开,最后都是拼接为了另一个系列 metamacro_if_eq0_ 宏,具体为 metamacro_if_eq0_ ## VALUE ,取决于 VALUE

    #define metamacro_if_eq0_20(...) metamacro_expand_
    
    #define metamacro_if_eq0_19(...) metamacro_expand_
    
    ...
    
    #define metamacro_if_eq0_2(...) metamacro_expand_
    
    #define metamacro_if_eq0_1(...) metamacro_expand_
    
    #define metamacro_if_eq0_0(...) __VA_ARGS__ metamacro_consume_
    

    可见,除了 metamacro_if_eq0_0 会替换为 __VA_ARGS__ metamacro_consume_ 外,其余都会替换为 metamacro_expand_

    #define metamacro_consume_(...)
    #define metamacro_expand_(...) __VA_ARGS__
    

    上面的宏都是为下面的这个宏服务的,其表示 A 如果和 B 相等,那么其后的参数将被使用,否则下一个参数会被使用。

    #define metamacro_if_eq(A, B) metamacro_concat(metamacro_if_eq, A)(B)
    

    metamacro_if_eq(1,1)(true)(false) 的展开过程:

    metamacro_if_eq(1,1)(true)(false) => metamacro_concat(metamacro_if_eq, 1)(1)(true)(false) => metamacro_if_eq1(1)(true)(false) => metamacro_if_eq0(metamacro_dec(1))(true)(false) => metamacro_if_eq0(0)(true)(false) => metamacro_concat(metamacro_if_eq0_, 0)(true)(false) => metamacro_if_eq0_0(true)(false) => true metamacro_consume_(false) => true

    metamacro_if_eq(1,2)(true)(false) 的展开过程:

    metamacro_if_eq(1,2)(true)(false) => metamacro_concat(metamacro_if_eq, 1)(2)(true)(false) => metamacro_if_eq1(2)(true)(false) => metamacro_if_eq0(metamacro_dec(2))(true)(false) => metamacro_if_eq0(1)(true)(false) => metamacro_concat(metamacro_if_eq0_, 1)(true)(false) => metamacro_if_eq0_1(true)(false) => metamacro_expand_(false) => false

    从展开的过程可知,AB 都必须是 0~20 的数字,包括 0 和 20 ,并且 B 不能小于 A

    #define metamacro_if_eq_recursive(A, B) metamacro_concat(metamacro_if_eq_recursive, A)(B)
    

    metamacro_if_eq_recursive 等同于 metamacro_if_eq ,可以用于规避递归宏展开的问题。

  17. 判断参数是否为偶数

    #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)
    

    N 的值必须是 0~20 并且包括 0 和 20 。

  18. 参数取反

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

    参数 B 取反,其必须是 0 或 1 。

RACEXTKeyPathCoding

  1. 校验并返回路径

    #define keypath(...) \
        metamacro_if_eq(1, metamacro_argcount(__VA_ARGS__)) \
        (keypath1(__VA_ARGS__)) \
        (keypath2(__VA_ARGS__))
    

    该宏使用 metamacro_if_eq 来判断传入的参数的个数是否是 1 ,如果是便使用 keypath1 ,否则使用 keypath2

    #define keypath1(PATH) (((void)(NO && ((void)PATH, NO)), strchr(# PATH, '.') + 1))
    

    这里使用了 (A,B) 语法,其中 A 和 B 都会编译执行,但是最后返回的是 B 的返回值。

    所以在编译过程中,会自动校验 PATH 的合法性。

    strchr(# PATH, '.') + 1 则是将 PATH 转换为字符串,并检索第一个 . 的位置,而后向后移动一位,得到去除了路径中的第一个部分的剩余路径。

    #define keypath2(OBJ, PATH) (((void)(NO && ((void)OBJ.PATH, NO)), # PATH))
    

    这里在编译过程中也会去检查路径的合法性,不同的是其会直接返回 PATH 字符串。

    这两个宏最后返回的都是字符串,可以在其前添加 @ 得到 NSString 对象。

    NSLog(@"%@",@keypath(self.view,superview));
        
    NSLog(@"%@",@keypath(self,view.superview));
        
    NSLog(@"%@",@keypath(self.view.superview));
    

    输出分别为:superviewview.superviewview.superview

    宏中所包含的 void 是为了消除警告

  2. 校验并返回集合路径

    #define collectionKeypath(...) \
        metamacro_if_eq(3, metamacro_argcount(__VA_ARGS__)) \
        (collectionKeypath3(__VA_ARGS__)) \
        (collectionKeypath4(__VA_ARGS__))
    

    首先,判断传递的参数是否是 3 个,如果是就使用 collectionKeypath3 宏,否则就使用 collectionKeypath4 宏。

    #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])
    

    这两个宏其实是对 keypath 宏的使用,如下面的例子:

    NSLog(@"%@",@collectionKeypath(self.array,NSArray.new,count));
    NSLog(@"%@",@collectionKeypath(self,array,NSArray.new,count));
    

    最后输出的都是:array.count ,其实这样使用该宏,同直接使用 keypath 宏没有什么区别,因为 NSLog(@"%@",@keypath(self.array.count)); 也输出同样的结果。

    该宏的设计是为了拼接集合中对象的路径,如下:

    NSLog(@"%@",@collectionKeypath(self.array,Person.new,name));
    NSLog(@"%@",@collectionKeypath(self,array,Person.new,name));
    

    最后返回 array.name

RACEXTScope

  1. 原生化

    #if DEBUG
    #define rac_keywordify autoreleasepool {}
    #else
    #define rac_keywordify try {} @catch (...) {}
    #endif
    

    ReactiveCocoa 框架中定义了一个 rac_keywordify 宏,来使相关宏的使用看起来更加原生,在定义其他宏时,使用该宏,那么使用定义的宏时,便可以添加 @ 使这个宏看起来更加原生。

  2. 清理任务设置

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

    使用 onExit 宏,可以很方便的为当前代码域添加处理任务。

    如下面的方法:

    - (void)test {
    	
    	NSLog(@"test start");
    	
    	@onExit {
    		NSLog(@"test end");
    	};
    }
    

    当在调试模式下时,其展开如下:

    - (void)test {
    	
    	NSLog(@"test start");
    	
    	@autoreleasepool {}
    	__strong rac_cleanupBlock_t metamacro_concat(rac_exitBlock_, __LINE__)
    	__attribute__((cleanup(rac_executeCleanupBlock), unused)) = ^{
    		NSLog(@"test end");
    	};
    }
    

    假设代码所处行号为 50 ,即 __LINE__ 为 50 ,那么进一步展开。

    - (void)test {
    	
    	NSLog(@"test start");
    	
    	@autoreleasepool {}
    	__strong rac_cleanupBlock_t rac_exitBlock_50
    	__attribute__((cleanup(rac_executeCleanupBlock), unused)) = ^{
    		NSLog(@"test end");
    	};
    }
    

    那么可知,onExit 宏,最终展开后,就是声明了一个 rac_cleanupBlock_t 类型的变量并初始化,且使用了 __attribute__ 字段指明在其作用域结束时,将其传递给指定的清理函数 rac_executeCleanupBlock

    typedef void (^rac_cleanupBlock_t)(void);
    
    static inline void rac_executeCleanupBlock (__strong rac_cleanupBlock_t *block) {
        (*block)();
    }
    
  3. 弱引用变量和强引用变量

    在 ReactiveCocoa 框架中,定义了两个宏,用来弱化和强化变量,从而防止循环引用问题。

    • 声明弱引用变量

      #define weakify(...) \
          rac_keywordify \
          metamacro_foreach_cxt(rac_weakify_,, __weak, __VA_ARGS__)
          
      #define unsafeify(...) \
          rac_keywordify \
          metamacro_foreach_cxt(rac_weakify_,, __unsafe_unretained, __VA_ARGS__)    
          
      #define rac_weakify_(INDEX, CONTEXT, VAR) \
          CONTEXT __typeof__(VAR) metamacro_concat(VAR, _weak_) = (VAR);    
      

      这里提供了两个宏,unsafeify 用来弱化不支持的 __weak 字段的变量,会使用 __unsafe_unretained 代替 __weak 字段修饰声明的变量。

      如在调试模式下,弱化 selfUIView *view = self.view; 的过程:

      @weakify(self,view) => 
      
      @autoreleasepool {} metamacro_foreach_cxt(rac_weakify_,, __weak, self, view) => 
      
      @autoreleasepool {} metamacro_concat(metamacro_foreach_cxt, 
      	metamacro_argcount(self, view))(rac_weakify_,, __weak, self, view) => 
      
      metamacro_foreach_cxt2(rac_weakify_,, __weak, self, view) =>
      
      metamacro_foreach_cxt1(rac_weakify_,,__weak, self) rac_weakify_(1, __weak, view) => 
      
      rac_weakify_(0, __weak, self) 
      	__weak __typeof__(view) metamacro_concat(view, _weak_) = (view); => 
      
      __weak __typeof__(self) metamacro_concat(self, _weak_) = (self);
      __weak __typeof__(view) view_weak_ = (view); => 
      
      __weak __typeof__(self) self_weak_ = (self);
      __weak __typeof__(view) view_weak_ = (view);
      
      

      可知,@weakify(self,view) 展开后的结果为

      __weak __typeof__(self) self_weak_ = (self); __weak __typeof__(view) view_weak_ = (view);

      即声明了两个弱引用变量 self_weakview_weak_ ,从这里也可知该宏的参数不能是包含的 . 的表达式,如 self.view ,否则,最后展开的变量为 self.view_weak 显然是错误的。

    • 声明强引用

      #define strongify(...) \
          rac_keywordify \
          _Pragma("clang diagnostic push") \
          _Pragma("clang diagnostic ignored \"-Wshadow\"") \
          metamacro_foreach(rac_strongify_,, __VA_ARGS__) \
          _Pragma("clang diagnostic pop")
          
      #define rac_strongify_(INDEX, VAR) \
          __strong __typeof__(VAR) VAR = metamacro_concat(VAR, _weak_);    
      

      该宏必须要和上面的弱引用宏配对使用,如 @strongify(self,view) 的展开

      @strongify(self,view) =>
      
      metamacro_foreach(rac_strongify_,, self,view) =>
      
      metamacro_foreach_cxt(metamacro_foreach_iter,, rac_strongify_, self, view) =>
      
      metamacro_concat(metamacro_foreach_cxt, metamacro_argcount(self, view))
      	(metamacro_foreach_iter,, rac_strongify_, self, view) =>
      	
      metamacro_foreach_cxt2(metamacro_foreach_iter,, rac_strongify_, self, view) =>
      
      metamacro_foreach_cxt1(metamacro_foreach_iter,, rac_strongify_, self)
      	metamacro_foreach_iter(1, rac_strongify_, view) =>
      	
      metamacro_foreach_iter(0, rac_strongify_, self) rac_strongify_(1, view) => 
      
      rac_strongify_(0, self) __strong __typeof__(view) view = metamacro_concat(view, _weak_); =>
      
      __strong __typeof__(self) self = metamacro_concat(self, _weak_);
      __strong __typeof__(view) view = view_weak_; =>
      
      __strong __typeof__(self) self = self_weak_;
      __strong __typeof__(view) view = view_weak_;
      

      最后展开结果为

      __strong __typeof__(self) self = self_weak_;__strong __typeof__(view) view = view_weak_;

      但是,这里可以看到存在对 self 和 view 的重复定义,所以两者不能在一个作用域内,并且应使用 _Pragma 消除命名重复警告。

      所以综上可知,

      - (void)test {
      	
      	UIView *view = self.view;
      	
      	@weakify(self,view)
      	    
      	@onExit {
      	    @strongify(self,view)
      	};
      }
      

      展开后为

      - (void)test {
      
      	UIView *view = self.view;
      	
      	__weak __typeof__(self) self_weak_ = (self);
      	__weak __typeof__(view) view_weak_ = (view);
      	    
      	@autoreleasepool {}
      	__strong rac_cleanupBlock_t rac_exitBlock_50
      	__attribute__((cleanup(rac_executeCleanupBlock), unused)) = ^{
      
      		_Pragma("clang diagnostic push") 
      		_Pragma("clang diagnostic ignored \"-Wshadow\"") 
      		
      		__strong __typeof__(self) self = self_weak_;
      		__strong __typeof__(view) view = view_weak_;
      		
      		_Pragma("clang diagnostic pop")
      	};
      }
      

关于宏的基本概念和用法可以参考OneV’s Den 的文章

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值