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);
}

運行結(jié)果:

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

結(jié)論:
1.一個#的時候跟是否有空格斑鸦,是否有小括號無關(guān)沙峻,只是把傳進來的東西(變量名k1至k8级解、字符串k9帅涂,C字符串k10)轉(zhuǎn)變成一個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);
    
}

運行結(jié)果:

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

結(jié)論:
在使用##的時候丛版,使用括號是返回不一樣的結(jié)果的巩掺,所以最好不要有括號


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);
}

運行結(jié)果

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);
}

運行結(jié)果:

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);
}

運行結(jié)果:

abc
c:3

結(jié)論:
就是對C語言的##的使用


metamacro_head

返回可變參數(shù)列表的第一個參數(shù):

/**
 * 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);
}

運行結(jié)果

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

按照注釋理解:獲取可變參數(shù)列表的第N(N最大值是20)個參數(shù),其中下標(biāo)是從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具體實現(xiàn)胖替,相當(dāng)于先刪除前面N個參數(shù)
測試代碼

- (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);
}

運行結(jié)果:

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

結(jié)論:
獲取參數(shù)列表的指定下標(biāo)的參數(shù),下標(biāo)是從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個關(guān)鍵的宏独令,
因為第一個調(diào)用第二個傳入的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);
    
    // 根據(jù)第一個宏的注釋可以了解到:
    // 當(dāng)沒有參數(shù)的時候,返回是錯誤的,返回的是1
    int count7 = metamacro_argcount();
    NSLog(@"count7:%zd", count7);
}

運行結(jié)果:

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

如果對 count7進行展開:

    // 出現(xiàn)錯誤
    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);
    // 出現(xiàn)錯誤
    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);
    // 出現(xiàn)錯誤
    int count10 = metamacro_head();
    NSLog(@"count10:%zd", count10);
    // 正確
    int count11 = metamacro_head_(0);
    NSLog(@"count11:%zd", count11);

總結(jié)
1.metamacro_argcount 必須最少要有一個參數(shù),最多有20個燃箭,如果是0個參數(shù)的會出現(xiàn)錯誤的結(jié)果冲呢,會得到1
2.因為metamacro_argcount是一個基本的宏,所以在RAC中很多的宏內(nèi)部都是使用到了metamacro_argcount這個宏招狸,所以那些宏也是不能讓可變參數(shù)個數(shù)是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);
}

運行結(jié)果:

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

當(dāng)是0的時候敬拓,輸出了一個錯誤的結(jié)果,所以范圍是(0,20]
就是把處于(0,20]的一個數(shù)減去一的操作裙戏。
但是:如果把代碼中的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操作信柿,居然實現(xiàn)的這么復(fù)雜冀偶,我只能想說這宏使用的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_(...)

按照定義就是相當(dāng)于完全忽略的啊
類似的用法是這樣的:

// 在Release環(huán)境一般需要如下定義
#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中的參數(shù)列表)的時候,會返回__VA_ARGS__ metamacro_consume_ 而在其他0_N的時候渔嚷,會返回metamacro_expand_(這個會忽略metamacro_if_eq0_N中的參數(shù)列表)

- (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

總結(jié):
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);
}

運行結(jié)果:

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

總結(jié):
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);
}

運行結(jié)果:

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);
}

運行結(jié)果:

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);
}

運行結(jié)果:

keypath2_1:view
keypath2_2:navigationBar
keypath2_3:navigationBar

總結(jié):

  1. keypath2(OBJ, PATH) 返回C字符串 “PATH"
  2. 發(fā)現(xiàn)這個keypath2中的OBJ參數(shù)在得到的結(jié)果中幾乎沒有什么用,但是實際上好像是有用途乃,好像是跟代碼提示有關(guān)绍傲,畢竟你在使用此宏的時候,會自動在PATH參數(shù)中彈出OBJ的相關(guān)屬性耍共,函數(shù)之類的烫饼。這個提示應(yīng)該跟OBJ.PATH有關(guān)。

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);
}

運行結(jié)果

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);
}

運行結(jié)果

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;
}

總結(jié):
代碼1等價于代碼3试读,4杠纵,5,6
RAC 理論是可以使用2個參數(shù)RAC(TARGET, KEYPATH)或者3個參數(shù)RAC(TARGET, KEYPATH,DEFAULTVALUE)钩骇,如果是使用2個參數(shù)的RAC那么就相當(dāng)于調(diào)用RAC(TARGET, KEYPATH,nil)比藻,當(dāng)然是可以有大于3個參數(shù)的情況铝量,這個時候,就相當(dāng)于前面3個參數(shù)是有效的银亲,如果是1個參數(shù)或者0個參數(shù)直接就是編譯不通過了

利用了中間類(RACSubscriptingAssignmentTrampoline)慢叨,最后調(diào)用了

[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宏又體現(xiàn)出強大的一面了群凶!


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外面會出現(xiàn)重復(fù)定義self這個error
        rac_strongify_(100, self) // 展開后: __strong __typeof__(self) self = self_weak_;
        // 這個self 相當(dāng)于重新定義成了 __strong, 不是原來定義的那個self了
        self.view.backgroundColor = [UIColor orangeColor];
    });
    
    // rac_weakify_ 有兩種使用方法,1:__weak 2:__unsafe_unretained
//    rac_weakify_(0, __unsafe_unretained, self)
}

主要的內(nèi)容都在代碼的注釋中描述了插爹。
rac_weakify_ 得到的是 self_weak_ 這個變量,而rac_strongify_定義的是 self這個變量(需要在block內(nèi)部中使用请梢,相當(dāng)于在block內(nèi)部重新定義了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");
}

運行結(jié)果

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

按照定義上注釋的大概意思是:使用@autoreleasepool會添加一個不必要的自動釋放池,而使用空的try/catch塊的時候會產(chǎn)生一個warning毅弧。但是在實際的測試代碼中并沒有發(fā)現(xiàn)這個warning气嫁,也許是Xcode9.2已經(jīng)優(yōu)化了這個?或者我對注釋的理解有錯誤够坐?


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個參數(shù)分別是:MACRO(其他的宏),SEP(分割符元咙,這里好像默認的是空字符梯影,不是空格),CONTEXT(上下文庶香,對于RAC的源碼來說主要是__weak和__unsafe_unretained)
    2.MACRO這個參數(shù)目前主要是指:
#define rac_weakify_(INDEX, CONTEXT, VAR) \
    CONTEXT __typeof__(VAR) metamacro_concat(VAR, _weak_) = (VAR);
  1. metamacro_foreach_cxtN 總共的參數(shù)是 3+N
  2. metamacro_foreach_cxt0 是什么都沒有做甲棍,metamacro_foreach_cxt1只是用給定的MACRO進行展開
    5.metamacro_foreach_cxtN(N>1)會使用metamacro_foreach_cxt(N-1)和MACRO進行展開。里面是需要注意MACRO的第一個參數(shù)(INDEX)是N-1
    6.根據(jù)5赶掖,metamacro_foreach_cxtN最終會使用了metamacro_foreach_cxt1感猛,感覺像遞歸調(diào)用一樣。奢赂。陪白。。

測試代碼:

// 先定義一個類似rac_weakify_ 的宏, #VAR 是轉(zhuǎn)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);
}

運行結(jié)果

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

根據(jù)定義可知膳灶,就是獲取可變參數(shù)列表的個數(shù)(當(dāng)參數(shù)為0時咱士,無法正確的獲取到)后,變化成metamacro_foreach_cxtN
測試代碼:

- (void)test_metamacro_foreach_cxt
{
    // 雖然編譯沒有問題,但是不能這么使用,因為在metamacro_foreach_cxt使用到了metamacro_argcount,所以在這里這個宏的參數(shù)最少是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);
}

運行結(jié)果

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的一步一步的展開

總結(jié):

  1. 最少是有一個參數(shù)(代碼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);
}

運行結(jié)果

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實現(xiàn)的這么啰里啰嗦聋迎,主要是因為這兩個是可以支持多個變量的__weak和__strong的聲明。


Demo:
https://github.com/xzjxylophone/RXVerifyExample.git
使用方法:
在MainViewController中枣耀,先添加這樣一行代碼:

image.png

所有的test實現(xiàn)都是在:
RXRACViewController 中

參考:
http://www.cocoachina.com/industry/20140621/8905.html
http://www.reibang.com/p/3d6c4416db5e
腦洞大開:https://blog.csdn.net/hopedark/article/details/20699723

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末霉晕,一起剝皮案震驚了整個濱河市庭再,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌牺堰,老刑警劉巖拄轻,帶你破解...
    沈念sama閱讀 210,835評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異伟葫,居然都是意外死亡恨搓,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,900評論 2 383
  • 文/潘曉璐 我一進店門筏养,熙熙樓的掌柜王于貴愁眉苦臉地迎上來斧抱,“玉大人,你說我怎么就攤上這事渐溶』云郑” “怎么了?”我有些...
    開封第一講書人閱讀 156,481評論 0 345
  • 文/不壞的土叔 我叫張陵茎辐,是天一觀的道長宪郊。 經(jīng)常有香客問我,道長拖陆,這世上最難降的妖魔是什么弛槐? 我笑而不...
    開封第一講書人閱讀 56,303評論 1 282
  • 正文 為了忘掉前任,我火速辦了婚禮依啰,結(jié)果婚禮上乎串,老公的妹妹穿的比我還像新娘。我一直安慰自己孔飒,他們只是感情好灌闺,可當(dāng)我...
    茶點故事閱讀 65,375評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著坏瞄,像睡著了一般桂对。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上鸠匀,一...
    開封第一講書人閱讀 49,729評論 1 289
  • 那天蕉斜,我揣著相機與錄音,去河邊找鬼缀棍。 笑死宅此,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的爬范。 我是一名探鬼主播父腕,決...
    沈念sama閱讀 38,877評論 3 404
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼青瀑!你這毒婦竟也來了璧亮?” 一聲冷哼從身側(cè)響起萧诫,我...
    開封第一講書人閱讀 37,633評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎枝嘶,沒想到半個月后帘饶,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,088評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡群扶,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,443評論 2 326
  • 正文 我和宋清朗相戀三年及刻,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片竞阐。...
    茶點故事閱讀 38,563評論 1 339
  • 序言:一個原本活蹦亂跳的男人離奇死亡缴饭,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出馁菜,到底是詐尸還是另有隱情茴扁,我是刑警寧澤,帶...
    沈念sama閱讀 34,251評論 4 328
  • 正文 年R本政府宣布汪疮,位于F島的核電站峭火,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏智嚷。R本人自食惡果不足惜卖丸,卻給世界環(huán)境...
    茶點故事閱讀 39,827評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望盏道。 院中可真熱鬧稍浆,春花似錦、人聲如沸猜嘱。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,712評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽朗伶。三九已至弦撩,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間论皆,已是汗流浹背益楼。 一陣腳步聲響...
    開封第一講書人閱讀 31,943評論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留点晴,地道東北人感凤。 一個月前我還...
    沈念sama閱讀 46,240評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像粒督,于是被迫代替她去往敵國和親陪竿。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,435評論 2 348

推薦閱讀更多精彩內(nèi)容