RAC知识点-宏

在RAC里面遇到了大量的宏定义
谨以此篇文章记录一下理解这些宏定义的过程

名词解释

变量名: 类似于 NSString *abc = @"def" 中的 abc
字符串: 这里主要指OC的字符串:@“def"
C字符串: ”def", 跟swift是一样的

C语言中一些小技巧与知识点

C知识点一:宏定义中的#

测试代码:

#define RACTest_SingleSharp1(A)  # A
#define RACTest_SingleSharp2(A)  #A
#define RACTest_SingleSharp3(A)  (# A)
#define RACTest_SingleSharp4(A)  (#A)
#define RACTest_SingleSharp5(A)  @#A
#define RACTest_SingleSharp6(A)  @# A
#define RACTest_SingleSharp7(A)  @(# A)
#define RACTest_SingleSharp8(A)  (@# A)
- (void)test_singleSharp
{
    NSString *k1 = @RACTest_SingleSharp1(abc);
    NSLog(@"k1:111%@111", k1);
    
    NSString *k2 = @RACTest_SingleSharp2(abc);
    NSLog(@"k2:111%@111", k2);
    
    NSString *k3 = @RACTest_SingleSharp3(abc);
    NSLog(@"k3:111%@111", k3);
    
    NSString *k4 = @RACTest_SingleSharp4(abc);
    NSLog(@"k4:111%@111", k4);
    
    NSString *k5 = RACTest_SingleSharp5(abc);
    NSLog(@"k5:111%@111", k5);
    
    NSString *k6 = RACTest_SingleSharp6(abc);
    NSLog(@"k6:111%@111", k6);
    
    NSString *k7 = RACTest_SingleSharp7(abc);
    NSLog(@"k7:111%@111", k7);
    
    NSString *k8 = RACTest_SingleSharp8(abc);
    NSLog(@"k8:111%@111", k8);

    NSString *k9 = RACTest_SingleSharp8(@"abc");
    NSLog(@"k9:111%@111", k9);
    
    NSString *k10 = RACTest_SingleSharp8("abc");
    NSLog(@"k10:111%@111", k10);

    NSString *k11 = [NSString stringWithUTF8String:RACTest_SingleSharp1(abc)];
    NSLog(@"k11:111%@111", k11);
}

运行结果:

2018-07-03 11:08:56.520593+0800 RXVerifyExample[2832:850629] k1:111abc111
2018-07-03 11:08:56.520600+0800 RXVerifyExample[2832:850629] k2:111abc111
2018-07-03 11:08:56.520614+0800 RXVerifyExample[2832:850629] k3:111abc111
2018-07-03 11:08:56.520628+0800 RXVerifyExample[2832:850629] k4:111abc111
2018-07-03 11:08:56.520636+0800 RXVerifyExample[2832:850629] k5:111abc111
2018-07-03 11:08:56.520642+0800 RXVerifyExample[2832:850629] k6:111abc111
2018-07-03 11:08:56.520656+0800 RXVerifyExample[2832:850629] k7:111abc111
2018-07-03 11:08:56.520664+0800 RXVerifyExample[2832:850629] k8:111abc111
2018-07-03 11:14:03.569841+0800 RXVerifyExample[2846:852872] k9:111@"abc"111
2018-07-03 11:14:03.569849+0800 RXVerifyExample[2846:852872] k10:111"abc"111
2018-07-03 11:16:06.778327+0800 RXVerifyExample[2851:853807] k11:111abc111

结论:
1.一个#的时候跟是否有空格,是否有小括号无关,只是把传进来的东西(变量名k1至k8、字符串k9,C字符串k10)转变成一个C的字符串(k1与k11证明)

  1. 单个#的时候,宏定义1,2,3,4写法都可以

C知识点二:宏定义中的##

测试代码:

#define RACTest_Add(A, B) (A + B)
#define RACTest_DoubleSharp1(A, B)   A ## B
#define RACTest_DoubleSharp2(A, B)   A##B
#define RACTest_DoubleSharp3(A, B)   (A ## B)
#define RACTest_DoubleSharp4(A, B)   (A##B)
- (void)test_doubleSharp
{
    NSString *RACTest_DoubleSharp1(test, String1) = @"abc";
    NSLog(@"testString1:%@", testString1);
    
    NSString *RACTest_DoubleSharp2(test, String2) = @"abc";
    NSLog(@"testString2:%@", testString2);
    
    NSString *RACTest_DoubleSharp3(test, String3) = @"abc";
    NSLog(@"testString3:%@", testString3);
    
    NSString *RACTest_DoubleSharp4(test, String4) = @"abc";
    NSLog(@"testString4:%@", testString4);
    
    NSString *(testString5) = @"abc";
    NSLog(@"testString5:%@", testString5);
    
    int c1 = RACTest_DoubleSharp1(RACTest_, Add)(1, 2);
    NSLog(@"c1:%zd", c1);
    
    int c2 = RACTest_DoubleSharp2(RACTest_, Add)(1, 2);
    NSLog(@"c2:%zd", c2);
    
//    // 有错误
//    int c3 = RACTest_DoubleSharp3(RACTest_, Add)(1, 2);
//    NSLog(@"c3:%zd", c3);
//
//    // 有错误
//    int c4 = RACTest_DoubleSharp4(RACTest_, Add)(1, 2);
//    NSLog(@"c4:%zd", c4);
//
//    // 有错误
//    int c5 = (RACTest_Add)(1, 2);
//    NSLog(@"c5:%zd", c5);
    
}

运行结果:

testString1:abc
testString2:abc
testString3:abc
testString4:abc
testString5:abc
c1:3
c2:3

结论:
在使用##的时候,使用括号是返回不一样的结果的,所以最好不要有括号


C知识点三:C语法中的逗号的一些特殊用法

- (void)test_commaInC
{
    int a = 0, b = 0;
    // 有warning
    a = 1, b = 2;
    // 有warning c=2
    int c = (a, b);
    // 没有warning d=2
    int d = ((void)a, b);
    NSLog(@"c:%zd, d:%zd", c, d);
}

运行结果

2018-07-04 11:30:32.521149+0800 RXVerifyExample[3877:1148601] c:2, d:2

C知识点四:strchr 与 字符串地址 + 1

测试代码:

- (void)test_strchr
{
    const char *source = "self.view";
    
    char *strchrResult1 = strchr(source, '.');
    NSLog(@"strchrResult1:%s", strchrResult1);
    
    char *strchrResult2 = strchr(source, '.') + 1;
    NSLog(@"strchrResult2:%s", strchrResult2);
    
    char *strchrResult3 = strchr(source, 'z');
    NSLog(@"strchrResult3:%s", strchrResult3);
}

运行结果:

strchrResult1:.view
strchrResult2:view
strchrResult3:(null)

metamacro_concat

涉及到的宏定义如下:

/**
 * Returns A and B concatenated after full macro expansion.
 */
// 第一个宏
#define metamacro_concat(A, B) \
        metamacro_concat_(A, B)
// 第二个宏
#define metamacro_concat_(A, B) A ## B

测试代码:

#define RACTest_Add(A, B) (A + B)
- (void)test_metamacro_concat
{
    NSString *metamacro_concat(test, StringA) = @"abc";
    NSLog(@"%@", testStringA);
    
    int c = metamacro_concat(RACTest_, Add)(1, 2);
    NSLog(@"c:%zd", c);
}

运行结果:

abc
c:3

结论:
就是对C语言的##的使用


metamacro_head

返回可变参数列表的第一个参数:

/**
 * Returns the first argument given. At least one argument must be provided.
 *
 * This is useful when implementing a variadic macro, where you may have only
 * one variadic argument, but no way to retrieve it (for example, because \c ...
 * always needs to match at least one argument).
 *
 * @code

#define varmacro(...) \
    metamacro_head(__VA_ARGS__)

 * @endcode
 */
// 第一个宏
#define metamacro_head(...) \
        metamacro_head_(__VA_ARGS__, 0)
// 第二个宏
#define metamacro_head_(FIRST, ...) FIRST

测试代码:

- (void)test_metamacro_head
{
    int head1 = metamacro_head(1111, 2222, 3333);
    NSLog(@"head1:%zd", head1);
    
    NSString *head2 = metamacro_head(@"abc", @"kkk", 123);
    NSLog(@"head2:%@", head2);
    
    NSString *metamacro_concat(head, metamacro_head(3Test, abc, @"abc", 11111)) = @"abc";
    NSLog(@"head3Test:%@", head3Test);
}

运行结果

head1:1111
head2:abc
head3Test:abc

metamacro_at

/**
 * Returns the Nth variadic argument (starting from zero). At least
 * N + 1 variadic arguments must be given. N must be between zero and twenty,
 * inclusive.
 */
#define metamacro_at(N, ...) \
        metamacro_concat(metamacro_at, N)(__VA_ARGS__)

按照注释理解:获取可变参数列表的第N(N最大值是20)个参数,其中下标是从0开始的。

#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_at3(_0, _1, _2, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at4(_0, _1, _2, _3, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at5(_0, _1, _2, _3, _4, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at6(_0, _1, _2, _3, _4, _5, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at7(_0, _1, _2, _3, _4, _5, _6, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at8(_0, _1, _2, _3, _4, _5, _6, _7, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at9(_0, _1, _2, _3, _4, _5, _6, _7, _8, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at10(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at11(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at12(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at13(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at14(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at15(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at16(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at17(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at18(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, ...) 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_atN(N是[0,20])是metamacro_at具体实现,相当于先删除前面N个参数
测试代码

- (void)test_metamacro_at
{
    int index1 = metamacro_at(1, 1, 2);
    NSLog(@"index1:%zd", index1);
    
    int index2 = metamacro_at(1, 1, 2, 3, 4, 5, 6, 7);
    NSLog(@"index2:%zd", index2);
    
    int index3 = metamacro_at(5, 0, 5, 11, 23, 34, 55);
    NSLog(@"index3:%zd", index3);
    
    int index4 = metamacro_at(20, 111, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21);
    NSLog(@"index4:%zd", index4);
    
    int index5 = metamacro_at0(0, 1);
    NSLog(@"index5:%zd", index5);
    
    int index6 = metamacro_at2(11, 22, 33);
    NSLog(@"index6:%zd", index6);
}

运行结果:

index1:2
index2:2
index3:55
index4:21
index5:0
index6:33

结论:
获取参数列表的指定下标的参数,下标是从0开始,且最大是等于20


metamacro_argcount

涉及到的宏定义如下:

/**
 * Returns the number of arguments (up to twenty) provided to the macro. At
 * least one argument must be provided.
 *
 * Inspired by P99: http://p99.gforge.inria.fr
 */
// 第一个宏
#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)

/**
 * Returns the Nth variadic argument (starting from zero). At least
 * N + 1 variadic arguments must be given. N must be between zero and twenty,
 * inclusive.
 */
// 第二个宏
#define metamacro_at(N, ...) \
        metamacro_concat(metamacro_at, N)(__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__)

/**
 * Returns the first argument given. At least one argument must be provided.
 *
 * This is useful when implementing a variadic macro, where you may have only
 * one variadic argument, but no way to retrieve it (for example, because \c ...
 * always needs to match at least one argument).
 *
 * @code

#define varmacro(...) \
    metamacro_head(__VA_ARGS__)

 * @endcode
 */
// 第四个宏
#define metamacro_head(...) \
        metamacro_head_(__VA_ARGS__, 0)
// 第五个宏
#define metamacro_head_(FIRST, ...) FIRST

总共涉及到5个关键的宏,
因为第一个调用第二个传入的N=20,因此第二个中的

metamacro_concat(metamacro_at, N)(__VA_ARGS__)

就等价于

metamacro_at20(__VA_ARGS__)

所以一个测试代码就是:

- (void)test_metamacro_argcount
{
    int count1 = metamacro_argcount(@"1", @"2");
    NSLog(@"count1:%zd", count1);
    
    int count2 = metamacro_at(20, @"1", @"2", 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1);
    NSLog(@"count2:%zd", count2);
    
    int count3 = metamacro_at20(@"1", @"2", 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1);
    NSLog(@"count3:%zd", count3);
    
    int count4 = metamacro_head(2, 1);
    NSLog(@"count4:%zd", count4);
    
    int count5 = metamacro_head_(2, 1, 0);
    NSLog(@"count5:%zd", count5);
    
    int count6 = metamacro_argcount(@"1");
    NSLog(@"count6:%zd", count6);
    
    // 根据第一个宏的注释可以了解到:
    // 当没有参数的时候,返回是错误的,返回的是1
    int count7 = metamacro_argcount();
    NSLog(@"count7:%zd", count7);
}

运行结果:

count1:2
count2:2
count3:2
count4:2
count5:2
count6:1
count7:1

如果对 count7进行展开:

    // 出现错误
    int count8 = metamacro_at(20, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1);
    NSLog(@"count8:%zd", count8);
    // 出现错误
    int count9 = metamacro_at20(20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1);
    NSLog(@"count3:%zd", count9);
    // 出现错误
    int count10 = metamacro_head();
    NSLog(@"count10:%zd", count10);
    // 正确
    int count11 = metamacro_head_(0);
    NSLog(@"count11:%zd", count11);

总结
1.metamacro_argcount 必须最少要有一个参数,最多有20个,如果是0个参数的会出现错误的结果,会得到1
2.因为metamacro_argcount是一个基本的宏,所以在RAC中很多的宏内部都是使用到了metamacro_argcount这个宏,所以那些宏也是不能让可变参数个数是0个


metamacro_dec

/**
 * Decrements VAL, which must be a number between zero and twenty, inclusive.
 *
 * This is primarily useful when dealing with indexes and counts in
 * metaprogramming.
 */
#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)

测试代码:

- (void)test_metamacro_dec
{
//    for (NSInteger i = 0; i <= 20; i++) {
//        // 这一行会有编译错误
//        int dec = metamacro_dec(i);
//        NSLog(@"i:%zd, dec:%zd", i, dec);
//    }
    int dec1 = metamacro_dec(0);
    NSLog(@"dec1:%zd", dec1);
    
    int dec2 = metamacro_dec(1);
    NSLog(@"dec2:%zd", dec2);
    
    int dec3 = metamacro_dec(5);
    NSLog(@"dec3:%zd", dec3);
    
    int dec4 = metamacro_dec(20);
    NSLog(@"dec4:%zd", dec4);
    
//    // error: metamacro_at21 不存在, 因为这个最大的就是metamacro_at20
//    int dec5 = metamacro_dec(21);
//    NSLog(@"dec5:%zd", dec5);
}

运行结果:

dec1:4294967295
dec2:0
dec3:4
dec4:19
dec5:4
dec6:4

当是0的时候,输出了一个错误的结果,所以范围是(0,20]
就是把处于(0,20]的一个数减去一的操作。
但是:如果把代码中的int换成:NSInteger的话,就会正确的得到-1了,因此如果直接使用这个宏的话,最好还是定义成NSInteger吧

有一个类似的宏:

/**
 * Increments VAL, which must be a number between zero and twenty, inclusive.
 *
 * This is primarily useful when dealing with indexes and counts in
 * metaprogramming.
 */
#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)

就是把N(范围在[0,20])加上1。
最后:简单的+1,-1操作,居然实现的这么复杂,我只能想说这宏使用的6666666666


metamacro_expand_

#define metamacro_expand_(...) __VA_ARGS__
- (void)test_metamacro_expand_
{
    NSString *expand1 = metamacro_expand_(@"abc");
    NSLog(@"expand1:%@", expand1);
    
    NSInteger expand2 = metamacro_expand_(123);
    NSLog(@"expand2:%zd", expand2);
}
2018-07-03 16:48:45.379045+0800 RXVerifyExample[3335:958417] expand1:abc
2018-07-03 16:48:45.379052+0800 RXVerifyExample[3335:958417] expand2:123

metamacro_consume_

#define metamacro_consume_(...)

按照定义就是相当于完全忽略的啊
类似的用法是这样的:

// 在Release环境一般需要如下定义
#define NSLog(...)
- (void)test_metamacro_consume_
{
    NSString *consume1 = metamacro_consume_(@"abc")@"def";
    NSLog(@"consume1:%@", consume1);
    
    NSInteger consume2 = metamacro_consume_(123)456;
    NSLog(@"consume2:%zd", consume2);
}
consume1:def
consume2:456

metamacro_if_eq0_N (N 属于[0,20])

#define metamacro_if_eq0_0(...) __VA_ARGS__ metamacro_consume_
#define metamacro_if_eq0_1(...) metamacro_expand_
#define metamacro_if_eq0_2(...) metamacro_expand_
#define metamacro_if_eq0_3(...) metamacro_expand_
#define metamacro_if_eq0_4(...) metamacro_expand_
#define metamacro_if_eq0_5(...) metamacro_expand_
#define metamacro_if_eq0_6(...) metamacro_expand_
#define metamacro_if_eq0_7(...) metamacro_expand_
#define metamacro_if_eq0_8(...) metamacro_expand_
#define metamacro_if_eq0_9(...) metamacro_expand_
#define metamacro_if_eq0_10(...) metamacro_expand_
#define metamacro_if_eq0_11(...) metamacro_expand_
#define metamacro_if_eq0_12(...) metamacro_expand_
#define metamacro_if_eq0_13(...) metamacro_expand_
#define metamacro_if_eq0_14(...) metamacro_expand_
#define metamacro_if_eq0_15(...) metamacro_expand_
#define metamacro_if_eq0_16(...) metamacro_expand_
#define metamacro_if_eq0_17(...) metamacro_expand_
#define metamacro_if_eq0_18(...) metamacro_expand_
#define metamacro_if_eq0_19(...) metamacro_expand_
#define metamacro_if_eq0_20(...) metamacro_expand_

按照定义可以知:只有在eq0_0(这个不会忽略metamacro_if_eq0_N中的参数列表)的时候,会返回__VA_ARGS__ metamacro_consume_ 而在其他0_N的时候,会返回metamacro_expand_(这个会忽略metamacro_if_eq0_N中的参数列表)

- (void)test_metamacro_if_eq0_N
{
    NSString *equal0_N_1 = metamacro_if_eq0_0(@"YES")(@"NO");
    NSLog(@"equal0_N_1:%@", equal0_N_1);
    
    NSString *equal0_N_2 = metamacro_if_eq0_1(@"YES")(@"NO");
    NSLog(@"equal0_N_2:%@", equal0_N_2);
    
    NSString *equal0_N_3 = metamacro_concat(metamacro_if_eq0_, 4)(@"YES")(@"NO");
    NSLog(@"equal0_N_3:%@", equal0_N_3);
    
    NSString *equal0_N_4 = metamacro_concat(metamacro_if_eq0_, 0)(@"YES")(@"NO");
    NSLog(@"equal0_N_4:%@", equal0_N_4);
}
equal0_N_1:YES
equal0_N_2:NO
equal0_N_3:NO
equal0_N_4:YES

总结:
X = metamacro_if_eqA_B(C)(D), 其中 A, B 属于[0,20], 如果A==B, 那么X=C否则X=D

metamacro_if_eqN(N 属于[0,20])

------------------------------
#define metamacro_if_eq0(VALUE) \
    metamacro_concat(metamacro_if_eq0_, VALUE)

#define metamacro_if_eq1(VALUE) metamacro_if_eq0(metamacro_dec(VALUE))
#define metamacro_if_eq2(VALUE) metamacro_if_eq1(metamacro_dec(VALUE))
#define metamacro_if_eq3(VALUE) metamacro_if_eq2(metamacro_dec(VALUE))
#define metamacro_if_eq4(VALUE) metamacro_if_eq3(metamacro_dec(VALUE))
#define metamacro_if_eq5(VALUE) metamacro_if_eq4(metamacro_dec(VALUE))
#define metamacro_if_eq6(VALUE) metamacro_if_eq5(metamacro_dec(VALUE))
#define metamacro_if_eq7(VALUE) metamacro_if_eq6(metamacro_dec(VALUE))
#define metamacro_if_eq8(VALUE) metamacro_if_eq7(metamacro_dec(VALUE))
#define metamacro_if_eq9(VALUE) metamacro_if_eq8(metamacro_dec(VALUE))
#define metamacro_if_eq10(VALUE) metamacro_if_eq9(metamacro_dec(VALUE))
#define metamacro_if_eq11(VALUE) metamacro_if_eq10(metamacro_dec(VALUE))
#define metamacro_if_eq12(VALUE) metamacro_if_eq11(metamacro_dec(VALUE))
#define metamacro_if_eq13(VALUE) metamacro_if_eq12(metamacro_dec(VALUE))
#define metamacro_if_eq14(VALUE) metamacro_if_eq13(metamacro_dec(VALUE))
#define metamacro_if_eq15(VALUE) metamacro_if_eq14(metamacro_dec(VALUE))
#define metamacro_if_eq16(VALUE) metamacro_if_eq15(metamacro_dec(VALUE))
#define metamacro_if_eq17(VALUE) metamacro_if_eq16(metamacro_dec(VALUE))
#define metamacro_if_eq18(VALUE) metamacro_if_eq17(metamacro_dec(VALUE))
#define metamacro_if_eq19(VALUE) metamacro_if_eq18(metamacro_dec(VALUE))
#define metamacro_if_eq20(VALUE) metamacro_if_eq19(metamacro_dec(VALUE))

按照定义可知:最后这一系列宏都会变成:

metamacro_if_eq0(VALUE)

而这个最终会变成:

metamacro_if_eq0_N

测试代码:

- (void)test_metamacro_if_eqN
{
    NSString *equalN_1 = metamacro_if_eq1(1)(@"YES")(@"NO");
    NSLog(@"equalN_1:%@", equalN_1);
    
    NSString *equalN_2 = metamacro_if_eq4(4)(@"YES")(@"NO");
    NSLog(@"equalN_2:%@", equalN_2);
    
    NSString *equalN_3 = metamacro_if_eq11(14)(@"YES")(@"NO");
    NSLog(@"equalN_3:%@", equalN_3);
    
    NSString *equalN_4 = metamacro_if_eq14(15)(@"YES")(@"NO");
    NSLog(@"equalN_4:%@", equalN_4);
    
//    // 14 大于 13, 按照逻辑,所以会报错!
//    NSString *equalN_5 = metamacro_if_eq14(13)(@"YES")(@"NO");
//    NSLog(@"equalN_5:%@", equalN_5);
}

运行结果:

equalN_1:YES
equalN_2:YES
equalN_3:NO
equalN_4:NO

总结:
X = metamacro_if_eqA(B)(C)(D), 其中 A, B 属于[0,20], 如果A==B, 那么X=C否则X=D


metamacro_if_eq

/**
 * If A is equal to B, the next argument list is expanded; otherwise, the
 * argument list after that is expanded. A and B must be numbers between zero
 * and twenty, inclusive. Additionally, B must be greater than or equal to A.
 *
 * @code

// expands to true
metamacro_if_eq(0, 0)(true)(false)

// expands to false
metamacro_if_eq(0, 1)(true)(false)

 * @endcode
 *
 * This is primarily useful when dealing with indexes and counts in
 * metaprogramming.
 */
#define metamacro_if_eq(A, B) \
        metamacro_concat(metamacro_if_eq, A)(B)

这里为什么B要大于等于A,就是因为会使用如下的这个

metamacro_if_eqN

测试代码:

- (void)test_metamacro_if_eq
{
    NSString *eq1 = metamacro_if_eq(1, 1)(@"YES")(@"NO");
    NSLog(@"eq1:%@", eq1);
    
    NSString *eq2 = metamacro_if_eq(1, 2)(@"YES")(@"NO");
    NSLog(@"eq2:%@", eq2);
}

运行结果:

eq1:YES
eq2:NO

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

这里运用到C知识点一和三
测试代码:

- (void)test_doubleAnd
{
    // self.view这里会有warning Code will newver be excuted
    NSInteger doubleAnd1 = NO &&((void)self.view, NO);
    NSLog(@"doubleAnd1:%zd", doubleAnd1);
    
    // self.view这里会有warning Code will newver be excuted
    NSString *doubleAnd2 = ((void)(NO &&((void)self.view, NO)), @"testValue");
    NSLog(@"doubleAnd2:%@", doubleAnd2);
}

运行结果:

doubleAnd1:0
doubleAnd2:testValue

keypath2

定义:

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

测试代码:

- (void)test_keypath2
{
    NSString *keypath2_1 = @keypath2(self, view);
    NSLog(@"keypath2_1:%@", keypath2_1);
    
    NSString *keypath2_2 = @keypath2(self.navigationController, navigationBar);
    NSLog(@"keypath2_2:%@", keypath2_2);
    
    NSString *keypath2_3 = [NSString stringWithUTF8String:keypath2(self.navigationController, navigationBar)];
    NSLog(@"keypath2_3:%@", keypath2_3);
}

运行结果:

keypath2_1:view
keypath2_2:navigationBar
keypath2_3:navigationBar

总结:

  1. keypath2(OBJ, PATH) 返回C字符串 “PATH"
  2. 发现这个keypath2中的OBJ参数在得到的结果中几乎没有什么用,但是实际上好像是有用,好像是跟代码提示有关,毕竟你在使用此宏的时候,会自动在PATH参数中弹出OBJ的相关属性,函数之类的。这个提示应该跟OBJ.PATH有关。

keypath1

定义

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

测试代码:

- (void)test_keypath1
{
    NSString *keypath1_1 = @keypath1(self.view);
    NSLog(@"keypath1_1:%@", keypath1_1);
    
    NSString *keypath1_2 = @keypath1(self.navigationController.navigationBar);
    NSLog(@"keypath1_2:%@", keypath1_2);
    
    NSString *keypath1_3 = [NSString stringWithUTF8String:keypath1(self.navigationController.navigationBar)];
    NSLog(@"keypath1_3:%@", keypath1_3);
}

运行结果

keypath1_1:view
keypath1_2:navigationController.navigationBar
keypath1_3:navigationController.navigationBar

keypath

定义


/**
 * \@keypath allows compile-time verification of key paths. Given a real object
 * receiver and key path:
 *
 * @code

NSString *UTF8StringPath = @keypath(str.lowercaseString.UTF8String);
// => @"lowercaseString.UTF8String"

NSString *versionPath = @keypath(NSObject, version);
// => @"version"

NSString *lowercaseStringPath = @keypath(NSString.new, lowercaseString);
// => @"lowercaseString"

 * @endcode
 *
 * ... the macro returns an \c NSString containing all but the first path
 * component or argument (e.g., @"lowercaseString.UTF8String", @"version").
 *
 * In addition to simply creating a key path, this macro ensures that the key
 * path is valid at compile-time (causing a syntax error if not), and supports
 * refactoring, such that changing the name of the property will also update any
 * uses of \@keypath.
 */
#define keypath(...) \
    metamacro_if_eq(1, metamacro_argcount(__VA_ARGS__))(keypath1(__VA_ARGS__))(keypath2(__VA_ARGS__))

测试代码:

- (void)test_keypath
{
    NSString *keypath_1 = @keypath(self, view);
    NSLog(@"keypath_1:%@", keypath_1);
    
    NSString *keypath_2 = @keypath(self.navigationController, navigationBar);
    NSLog(@"keypath_2:%@", keypath_2);
    
    NSString *keypath_3 = @keypath(self.view);
    NSLog(@"keypath_3:%@", keypath_3);
    
    NSString *keypath_4 = @keypath(self.navigationController.navigationBar);
    NSLog(@"keypath_4:%@", keypath_4);
}

运行结果

keypath_1:view
keypath_2:navigationBar
keypath_3:view
keypath_4:navigationController.navigationBar

RAC和RAC_

定义


/// Assigns a signal to an object property, automatically setting the given key
/// path on every `next`. When the signal completes, the binding is automatically
/// disposed of.
///
/// There are two different versions of this macro:
///
///  - RAC(TARGET, KEYPATH, NILVALUE) will bind the `KEYPATH` of `TARGET` to the
///    given signal. If the signal ever sends a `nil` value, the property will be
///    set to `NILVALUE` instead. `NILVALUE` may itself be `nil` for object
///    properties, but an NSValue should be used for primitive properties, to
///    avoid an exception if `nil` is sent (which might occur if an intermediate
///    object is set to `nil`).
///  - RAC(TARGET, KEYPATH) is the same as the above, but `NILVALUE` defaults to
///    `nil`.
///
/// See -[RACSignal setKeyPath:onObject:nilValue:] for more information about the
/// binding's semantics.
///
/// Examples
///
///  RAC(self, objectProperty) = objectSignal;
///  RAC(self, stringProperty, @"foobar") = stringSignal;
///  RAC(self, integerProperty, @42) = integerSignal;
///
/// WARNING: Under certain conditions, use of this macro can be thread-unsafe.
///          See the documentation of -setKeyPath:onObject:nilValue:.
#define RAC(TARGET, ...) \
    metamacro_if_eq(1, metamacro_argcount(__VA_ARGS__)) \
        (RAC_(TARGET, __VA_ARGS__, nil)) \
        (RAC_(TARGET, __VA_ARGS__))

/// Do not use this directly. Use the RAC macro above.
#define RAC_(TARGET, KEYPATH, NILVALUE) \
    [[RACSubscriptingAssignmentTrampoline alloc] initWithTarget:(TARGET) nilValue:(NILVALUE)][@keypath(TARGET, KEYPATH)]

测试代码:

- (void)test_RAC
{
    RACSignal *signal = [self.textFieldSignal map:^id _Nullable(id  _Nullable value) {
        return [value boolValue] ? [UIColor redColor] : [UIColor greenColor];
    }];
    
    // 编译错误
//    RAC(self.textField) = signal;
    
    // 代码1
    RAC(self.textField, backgroundColor, [UIColor blueColor]) = signal;
    
//    // 代码2
//    RAC(self.textField, backgroundColor) = signal;
//
//    // 代码3
//    RAC_(self.textField, backgroundColor, [UIColor blueColor]) = signal;
    
    // 代码4
//    [[RACSubscriptingAssignmentTrampoline alloc] initWithTarget:(self.textField) nilValue:[UIColor blueColor]][@"backgroundColor"] = signal;
    
    // 代码5
//    RACSubscriptingAssignmentTrampoline *tmp = [[RACSubscriptingAssignmentTrampoline alloc] initWithTarget:(self.textField) nilValue:[UIColor blueColor]];
//    tmp[@"backgroundColor"] = signal;
    
    // 代码6
//    RACSubscriptingAssignmentTrampoline *tmp = [[RACSubscriptingAssignmentTrampoline alloc] initWithTarget:(self.textField) nilValue:[UIColor blueColor]];
//    [tmp setObject:signal forKeyedSubscript:@"backgroundColor"];
    
    // 代码7:crash
//    RACSubscriptingAssignmentTrampoline *tmp1 = [[RACSubscriptingAssignmentTrampoline alloc] initWithTarget:(self.textField) nilValue:[UIColor blueColor]];
//    [tmp1 setValue:signal forKey:@"backgroundColor"];
//
    // 代码8:crash
//    RACSubscriptingAssignmentTrampoline *tmp2 = [[RACSubscriptingAssignmentTrampoline alloc] initWithTarget:(self.textField) nilValue:[UIColor blueColor]];
//    [tmp2 setValue:signal forKeyPath:@"backgroundColor"];
    
    // 代码9  验证defaultValue的
//    RACSignal *signal2 = [self.textFieldSignal map:^id _Nullable(id  _Nullable value) {
//        return [value boolValue] ? [UIColor redColor] : nil;
//    }];
//    RACSubscriptingAssignmentTrampoline *tmp = [[RACSubscriptingAssignmentTrampoline alloc] initWithTarget:(self.textField) nilValue:[UIColor blueColor]];
//    tmp[@"backgroundColor"] = signal2;
}

总结:
代码1等价于代码3,4,5,6
RAC 理论是可以使用2个参数RAC(TARGET, KEYPATH)或者3个参数RAC(TARGET, KEYPATH,DEFAULTVALUE),如果是使用2个参数的RAC那么就相当于调用RAC(TARGET, KEYPATH,nil),当然是可以有大于3个参数的情况,这个时候,就相当于前面3个参数是有效的,如果是1个参数或者0个参数直接就是编译不通过了

利用了中间类(RACSubscriptingAssignmentTrampoline),最后调用了

[signal setKeyPath:keyPath onObject:self.target nilValue:self.nilValue];

这里面做了信号的订阅。


RACObserve

定义:

/// Creates a signal which observes `KEYPATH` on `TARGET` for changes.
///
/// In either case, the observation continues until `TARGET` _or self_ is
/// deallocated. If any intermediate object is deallocated instead, it will be
/// assumed to have been set to nil.
///
/// Make sure to `@strongify(self)` when using this macro within a block! The
/// macro will _always_ reference `self`, which can silently introduce a retain
/// cycle within a block. As a result, you should make sure that `self` is a weak
/// reference (e.g., created by `@weakify` and `@strongify`) before the
/// expression that uses `RACObserve`.
///
/// Examples
///
///    // Observes self, and doesn't stop until self is deallocated.
///    RACSignal *selfSignal = RACObserve(self, arrayController.items);
///
///    // Observes the array controller, and stops when self _or_ the array
///    // controller is deallocated.
///    RACSignal *arrayControllerSignal = RACObserve(self.arrayController, items);
///
///    // Observes obj.arrayController, and stops when self _or_ the array
///    // controller is deallocated.
///    RACSignal *signal2 = RACObserve(obj.arrayController, items);
///
///    @weakify(self);
///    RACSignal *signal3 = [anotherSignal flattenMap:^(NSArrayController *arrayController) {
///        // Avoids a retain cycle because of RACObserve implicitly referencing
///        // self.
///        @strongify(self);
///        return RACObserve(arrayController, items);
///    }];
///
/// Returns a signal which sends the current value of the key path on
/// subscription, then sends the new value every time it changes, and sends
/// completed if self or observer is deallocated.
#define _RACObserve(TARGET, KEYPATH) \
({ \
    __weak id target_ = (TARGET); \
    [target_ rac_valuesForKeyPath:@keypath(TARGET, KEYPATH) observer:self]; \
})

#if __clang__ && (__clang_major__ >= 8)
#define RACObserve(TARGET, KEYPATH) _RACObserve(TARGET, KEYPATH)
#else
#define RACObserve(TARGET, KEYPATH) \
({ \
    _Pragma("clang diagnostic push") \
    _Pragma("clang diagnostic ignored \"-Wreceiver-is-weak\"") \
    _RACObserve(TARGET, KEYPATH) \
    _Pragma("clang diagnostic pop") \
})
#endif

测试代码:

- (void)test_RACObserve
{
    // 代码1
    [RACObserve(self.view, backgroundColor) subscribeNext:^(id  _Nullable x) {
        NSLog(@"RACObserve:%@", x);
    }];
    
    // 代码2
//    [[self.view rac_valuesForKeyPath:@"backgroundColor" observer:self] subscribeNext:^(id  _Nullable x) {
//        NSLog(@"rac_valuesForKeyPath:%@", x);
//    }];
}

这里的keypath宏又体现出强大的一面了!


rac_weakify_与rac_strongify_

定义

#define rac_weakify_(INDEX, CONTEXT, VAR) \
    CONTEXT __typeof__(VAR) metamacro_concat(VAR, _weak_) = (VAR);

#define rac_strongify_(INDEX, VAR) \
    __strong __typeof__(VAR) VAR = metamacro_concat(VAR, _weak_);

测试代码:

- (void)test_rac_weakify_And_rac_strongify_
{
    // 这个放在block外面
    rac_weakify_(0, __weak, self) // 展开后: __weak __typeof__(self) self_weak_ = (self);
    self_weak_.view.backgroundColor = [UIColor grayColor];

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        // 如果这一行代码如果放到block外面会出现重复定义self这个error
        rac_strongify_(100, self) // 展开后: __strong __typeof__(self) self = self_weak_;
        // 这个self 相当于重新定义成了 __strong, 不是原来定义的那个self了
        self.view.backgroundColor = [UIColor orangeColor];
    });
    
    // rac_weakify_ 有两种使用方法,1:__weak 2:__unsafe_unretained
//    rac_weakify_(0, __unsafe_unretained, self)
}

主要的内容都在代码的注释中描述了。
rac_weakify_ 得到的是 self_weak_ 这个变量,而rac_strongify_定义的是 self这个变量(需要在block内部中使用,相当于在block内部重新定义了self),类似于下面这段代码

- (void)test_redefineVar
{
    int a = 0;
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        int a = 4;
        NSLog(@"a in block:%zd", a);
    });
    NSLog(@"a not in block:%zd", a);
}

rac_keywordify

定义:

// Details about the choice of backing keyword:
//
// The use of @try/@catch/@finally can cause the compiler to suppress
// return-type warnings.
// The use of @autoreleasepool {} is not optimized away by the compiler,
// resulting in superfluous creation of autorelease pools.
//
// Since neither option is perfect, and with no other alternatives, the
// compromise is to use @autorelease in DEBUG builds to maintain compiler
// analysis, and to use @try/@catch otherwise to avoid insertion of unnecessary
// autorelease pools.
#if DEBUG
#define rac_keywordify autoreleasepool {}
#else
#define rac_keywordify try {} @catch (...) {}
#endif

测试代码:

#define RACTest_rac_keywordify try {} @catch (...) {}
- (void)test_rac_keywordify
{
    @rac_keywordify
    NSLog(@"do something after rac_keywordify");
    
    @autoreleasepool{}
    NSLog(@"do something after autoreleasepool");
    
    @RACTest_rac_keywordify
    NSLog(@"do something after RACTest_rac_keywordify");
    
    @try {}
    @catch(...) {}
    NSLog(@"do something after try/catch");
}

运行结果

do something after rac_keywordify
do something after autoreleasepool
do something after RACTest_rac_keywordify
do something after try/catch

按照定义上注释的大概意思是:使用@autoreleasepool会添加一个不必要的自动释放池,而使用空的try/catch块的时候会产生一个warning。但是在实际的测试代码中并没有发现这个warning,也许是Xcode9.2已经优化了这个?或者我对注释的理解有错误?


metamacro_foreach_cxtN(N属于[0,20])

定义:cxt是context的缩写

// metamacro_foreach_cxt expansions
#define metamacro_foreach_cxt0(MACRO, SEP, CONTEXT)
#define metamacro_foreach_cxt1(MACRO, SEP, CONTEXT, _0) MACRO(0, CONTEXT, _0)

#define metamacro_foreach_cxt2(MACRO, SEP, CONTEXT, _0, _1) \
    metamacro_foreach_cxt1(MACRO, SEP, CONTEXT, _0) \
    SEP \
    MACRO(1, CONTEXT, _1)

#define metamacro_foreach_cxt3(MACRO, SEP, CONTEXT, _0, _1, _2) \
    metamacro_foreach_cxt2(MACRO, SEP, CONTEXT, _0, _1) \
    SEP \
    MACRO(2, CONTEXT, _2)

#define metamacro_foreach_cxt4(MACRO, SEP, CONTEXT, _0, _1, _2, _3) \
    metamacro_foreach_cxt3(MACRO, SEP, CONTEXT, _0, _1, _2) \
    SEP \
    MACRO(3, CONTEXT, _3)

#define metamacro_foreach_cxt5(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4) \
    metamacro_foreach_cxt4(MACRO, SEP, CONTEXT, _0, _1, _2, _3) \
    SEP \
    MACRO(4, CONTEXT, _4)
// ..... 一直到metamacro_foreach_cxt20都是类似的,具体可以看RAC源码

由上述定义可知:

  1. 所有的宏定义前3个参数分别是:MACRO(其他的宏),SEP(分割符,这里好像默认的是空字符,不是空格),CONTEXT(上下文,对于RAC的源码来说主要是__weak和__unsafe_unretained)
    2.MACRO这个参数目前主要是指:
#define rac_weakify_(INDEX, CONTEXT, VAR) \
    CONTEXT __typeof__(VAR) metamacro_concat(VAR, _weak_) = (VAR);
  1. metamacro_foreach_cxtN 总共的参数是 3+N
  2. metamacro_foreach_cxt0 是什么都没有做,metamacro_foreach_cxt1只是用给定的MACRO进行展开
    5.metamacro_foreach_cxtN(N>1)会使用metamacro_foreach_cxt(N-1)和MACRO进行展开。里面是需要注意MACRO的第一个参数(INDEX)是N-1
    6.根据5,metamacro_foreach_cxtN最终会使用了metamacro_foreach_cxt1,感觉像递归调用一样。。。。

测试代码:

// 先定义一个类似rac_weakify_ 的宏, #VAR 是转C字符串, @#VAR 是一个OC字符串
#define RACTest_rac_weakify_(INDEX, CONTEXT, VAR)  NSString *metamacro_concat(CONTEXT, metamacro_concat(VAR, INDEX)) = @#VAR;
- (void)test_metamacro_foreach_cxtN
{
    // 测试这个宏
    RACTest_rac_weakify_(10, tmp, Abc)
    NSLog(@"value:%@", tmpAbc10);
    
    // 什么也没有
    metamacro_foreach_cxt0(RACTest_rac_weakify_,, testContext)
    
    NSString *object = @"abc";
    NSLog(@"object:%@", object);
    // 生成了1个变量
    metamacro_foreach_cxt1(RACTest_rac_weakify_,, testContext, object)
    NSLog(@"testContextobject0:%@", testContextobject0);
    
    NSString *objectA = @"abc";
    NSString *objectB = @"abc";
    NSString *objectC = @"abc";
    NSString *objectD = @"abc";
    NSLog(@"objectA:%@, objectB:%@, objectC:%@, objectD:%@", objectA, objectB, objectC, objectD);
    // 生成了4个变量
    metamacro_foreach_cxt4(RACTest_rac_weakify_,, testContext, objectA, objectB, objectC, objectD)
    NSLog(@"testContextobjectA0:%@", testContextobjectA0);
    NSLog(@"testContextobjectB1:%@", testContextobjectB1);
    NSLog(@"testContextobjectC2:%@", testContextobjectC2);
    NSLog(@"testContextobjectD3:%@", testContextobjectD3);
}

运行结果

value:Abc
object:abc
testContextobject0:object
objectA:abc, objectB:abc, objectC:abc, objectD:abc
testContextobjectA0:objectA
testContextobjectB1:objectB
testContextobjectC2:objectC
testContextobjectD3:objectD

metamacro_foreach_cxt

定义:

/**
 * For each consecutive variadic argument (up to twenty), MACRO is passed the
 * zero-based index of the current argument, CONTEXT, and then the argument
 * itself. The results of adjoining invocations of MACRO are then separated by
 * SEP.
 *
 * Inspired by P99: http://p99.gforge.inria.fr
 */
#define metamacro_foreach_cxt(MACRO, SEP, CONTEXT, ...) \
        metamacro_concat(metamacro_foreach_cxt, metamacro_argcount(__VA_ARGS__))(MACRO, SEP, CONTEXT, __VA_ARGS__)

根据定义可知,就是获取可变参数列表的个数(当参数为0时,无法正确的获取到)后,变化成metamacro_foreach_cxtN
测试代码:

- (void)test_metamacro_foreach_cxt
{
    // 虽然编译没有问题,但是不能这么使用,因为在metamacro_foreach_cxt使用到了metamacro_argcount,所以在这里这个宏的参数最少是4个,要不然使用会有问题
    // 证明就是在无法编译: @weakify()
    metamacro_foreach_cxt(RACTest_rac_weakify_,,testContext)
//    metamacro_foreach_cxt0(RACTest_rac_weakify_,,testContext)
    
    NSString *objectA = @"abc";
    NSString *objectB = @"abc";
    NSString *objectC = @"abc";
    NSString *objectD = @"abc";
    NSLog(@"objectA:%@, objectB:%@, objectC:%@, objectD:%@", objectA, objectB, objectC, objectD);
    // 生成了4个变量
    metamacro_foreach_cxt(RACTest_rac_weakify_,, testContext, objectA, objectB, objectC, objectD)
    NSLog(@"testContextobjectA0:%@", testContextobjectA0);
    NSLog(@"testContextobjectB1:%@", testContextobjectB1);
    NSLog(@"testContextobjectC2:%@", testContextobjectC2);
    NSLog(@"testContextobjectD3:%@", testContextobjectD3);
}

运行结果

objectA:abc, objectB:abc, objectC:abc, objectD:abc
testContextobjectA0:objectA
testContextobjectB1:objectB
testContextobjectC2:objectC
testContextobjectD3:objectD

weakify

定义与涉及到的宏:

/**
 * Creates \c __weak shadow variables for each of the variables provided as
 * arguments, which can later be made strong again with #strongify.
 *
 * This is typically used to weakly reference variables in a block, but then
 * ensure that the variables stay alive during the actual execution of the block
 * (if they were live upon entry).
 *
 * See #strongify for an example of usage.
 */
#define weakify(...) \
    rac_keywordify \
    metamacro_foreach_cxt(rac_weakify_,, __weak, __VA_ARGS__)
/**
 * For each consecutive variadic argument (up to twenty), MACRO is passed the
 * zero-based index of the current argument, CONTEXT, and then the argument
 * itself. The results of adjoining invocations of MACRO are then separated by
 * SEP.
 *
 * Inspired by P99: http://p99.gforge.inria.fr
 */
#define metamacro_foreach_cxt(MACRO, SEP, CONTEXT, ...) \
        metamacro_concat(metamacro_foreach_cxt, metamacro_argcount(__VA_ARGS__))(MACRO, SEP, CONTEXT, __VA_ARGS__)
#define metamacro_foreach_cxt1(MACRO, SEP, CONTEXT, _0) MACRO(0, CONTEXT, _0)
#define rac_weakify_(INDEX, CONTEXT, VAR) \
    CONTEXT __typeof__(VAR) metamacro_concat(VAR, _weak_) = (VAR);

测试代码:

- (void)test_weakify
{
    // 代码1
//    @weakify(self)
    
    // 代码2
//    @autoreleasepool{}
//    metamacro_foreach_cxt(rac_weakify_,, __weak, self)

    // 代码3
//    @autoreleasepool{}
//    metamacro_concat(metamacro_foreach_cxt, 1)(rac_weakify_,, __weak, self)

    // 代码4
//    @autoreleasepool{}
//    metamacro_foreach_cxt1(rac_weakify_,, __weak, self)
    
    // 代码5
//    @autoreleasepool{}
//    rac_weakify_(0, __weak, self)
    
    // 代码6:无法编译
//    @weakify()
    
    // 代码7:无法编译
//    @weakify(self.view)
    
    // 代码8
    NSObject *tmpObject = [NSObject new];
    @weakify(tmpObject, self);
    NSLog(@"tmpObject_weak_:%@", tmpObject_weak_);
    NSLog(@"self_weak_:%@", self_weak_);
}

代码1至代码5是对weakify的一步一步的展开

总结:

  1. 最少是有一个参数(代码6)
  2. 不能使用self.view这种属性方式(代码7)
  3. 可以批量的定义__weak(代码8)

metamacro_foreach_iter

定义:

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

测试代码:

#define RACTest_Incream(INDEX, ARG) (INDEX + ARG - 1)
- (void)test_metamacro_foreach_iter
{
    NSInteger iter1 = metamacro_foreach_iter(5, RACTest_Incream, 10);
    NSLog(@"iter1:%zd", iter1);
}

运行结果

2018-07-05 17:54:51.871614+0800 RXVerifyExample[4997:1534307] iter1:14

strongify

定义:

/**
 * Strongly references each of the variables provided as arguments, which must
 * have previously been passed to #weakify.
 *
 * The strong references created will shadow the original variable names, such
 * that the original names can be used without issue (and a significantly
 * reduced risk of retain cycles) in the current scope.
 *
 * @code

    id foo = [[NSObject alloc] init];
    id bar = [[NSObject alloc] init];

    @weakify(foo, bar);

    // this block will not keep 'foo' or 'bar' alive
    BOOL (^matchesFooOrBar)(id) = ^ BOOL (id obj){
        // but now, upon entry, 'foo' and 'bar' will stay alive until the block has
        // finished executing
        @strongify(foo, bar);

        return [foo isEqual:obj] || [bar isEqual:obj];
    };

 * @endcode
 */
#define strongify(...) \
    rac_keywordify \
    _Pragma("clang diagnostic push") \
    _Pragma("clang diagnostic ignored \"-Wshadow\"") \
    metamacro_foreach(rac_strongify_,, __VA_ARGS__) \
    _Pragma("clang diagnostic pop")

测试代码:

- (void)test_strongify
{
    @weakify(self)
    self_weak_.view.backgroundColor = [UIColor grayColor];
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        
        // 代码1
//        @strongify(self)
        
        // 代码2
//        metamacro_foreach(rac_strongify_,, self)
        
        // 代码3
//        metamacro_foreach_cxt(metamacro_foreach_iter,, rac_strongify_, self)
        
        // 代码4
//        metamacro_foreach_cxt1(metamacro_foreach_iter,, rac_strongify_, self)
        
        // 代码5
//        metamacro_foreach_iter(0, rac_strongify_, self)
        
        // 代码6
        rac_strongify_(0, self)
        
        self.view.backgroundColor = [UIColor orangeColor];
    });
}

看注释和跟weakify比较,绝大部分都是类似的,只是strongify最后是使用metamacro_foreach_iter对rac_strongify_展开。
weakify和strongify实现的这么啰里啰嗦,主要是因为这两个是可以支持多个变量的__weak和__strong的声明。


Demo:
https://github.com/xzjxylophone/RXVerifyExample.git
使用方法:
在MainViewController中,先添加这样一行代码:

image.png

所有的test实现都是在:
RXRACViewController 中

参考:
http://www.cocoachina.com/industry/20140621/8905.html
https://www.jianshu.com/p/3d6c4416db5e
脑洞大开:https://blog.csdn.net/hopedark/article/details/20699723

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,657评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,662评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,143评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,732评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,837评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,036评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,126评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,868评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,315评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,641评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,773评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,470评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,126评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,859评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,095评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,584评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,676评论 2 351

推荐阅读更多精彩内容