在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撕瞧,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é):
- keypath2(OBJ, PATH) 返回C字符串 “PATH"
- 發(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源碼
由上述定義可知:
- 所有的宏定義前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);
- metamacro_foreach_cxtN 總共的參數(shù)是 3+N
- 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é):
- 最少是有一個參數(shù)(代碼6)
- 不能使用self.view這種屬性方式(代碼7)
- 可以批量的定義__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中枣耀,先添加這樣一行代碼:
所有的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