EasyTuple
是由美团开源的一个第三方库,它给Objective-C 添加了元组的能力,可以将几个对象包裹在一个对象中,这样我们就可以从一个函数中返回多个值。它的使用非常简单,比如我们想创建一个由两个元素组成的元组,那么可以这样写:
1 2 |
EZTuple2<NSNumber *, NSString *> *tuple = EZTuple(@1, @"string"); |
如果使用 Xcode 辅助编辑器查看预编译后的代码,那么上面的例子在预编译后,会被展开为
1 2 |
EZTuple2<NSNumber *, NSString *> *tuple = [[EZTuple2 alloc] initWithFirst:@1 second:@"string"]; |
可以看到原来的宏的写法会自动被转换成 Objective-C 中的类的创建语法了,那么这个转换过程是怎样发生的呢?下面让我们一步步地去分析这个转换的过程。
EZTuple
右边这个看起来像函数的EZTuple
,其实是一个宏:
1 2 |
#define EZTuple(...) EZTupleAs(EZ_CONCAT(EZTuple, EZ_ARG_COUNT(__VA_ARGS__)), __VA_ARGS__) |
EZ_CONCAT
我们遇到的第一个宏就是 EZ_CONCAT
,它的定义如下
1 2 3 |
#define EZ_CONCAT(A, B) EZ_CONCAT_(A, B) #define EZ_CONCAT_(A, B) A ## B |
作用是把 A 和 B 两个字符串连接到一起,比如
EZ_CONCAT(hello, world)
的结果就是helloworld
。
EZ_ARG_COUNT
EZ_ARG_COUNT
是我们遇到的第二个宏,它的定义有些复杂:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
#define EZ_ARG_COUNT(...) _EZ_ARG_COUNT(__VA_ARGS__) #define _EZ_ARG_COUNT(...) EZ_ARG_AT(20, ##__VA_ARGS__, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) #define EZ_ARG_AT(N, ...) EZ_ARG_AT_(N, __VA_ARGS__) #define EZ_ARG_AT_(N, ...) EZ_CONCAT(EZ_ARG_AT, N)(__VA_ARGS__) #define EZ_ARG_AT0(...) EZ_ARG_HEAD(__VA_ARGS__) #define EZ_ARG_AT1(_0, ...) EZ_ARG_HEAD(__VA_ARGS__) #define EZ_ARG_AT2(_0, _1, ...) EZ_ARG_HEAD(__VA_ARGS__) #define EZ_ARG_AT3(_0, _1, _2, ...) EZ_ARG_HEAD(__VA_ARGS__) #define EZ_ARG_AT4(_0, _1, _2, _3, ...) EZ_ARG_HEAD(__VA_ARGS__) #define EZ_ARG_AT5(_0, _1, _2, _3, _4, ...) EZ_ARG_HEAD(__VA_ARGS__) #define EZ_ARG_AT6(_0, _1, _2, _3, _4, _5, ...) EZ_ARG_HEAD(__VA_ARGS__) #define EZ_ARG_AT7(_0, _1, _2, _3, _4, _5, _6, ...) EZ_ARG_HEAD(__VA_ARGS__) #define EZ_ARG_AT8(_0, _1, _2, _3, _4, _5, _6, _7, ...) EZ_ARG_HEAD(__VA_ARGS__) #define EZ_ARG_AT9(_0, _1, _2, _3, _4, _5, _6, _7, _8, ...) EZ_ARG_HEAD(__VA_ARGS__) #define EZ_ARG_AT10(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, ...) \ EZ_ARG_HEAD(__VA_ARGS__) #define EZ_ARG_AT11(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, ...) \ EZ_ARG_HEAD(__VA_ARGS__) #define EZ_ARG_AT12(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, ...) \ EZ_ARG_HEAD(__VA_ARGS__) #define EZ_ARG_AT13(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, ...) \ EZ_ARG_HEAD(__VA_ARGS__) #define EZ_ARG_AT14(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, ...) \ EZ_ARG_HEAD(__VA_ARGS__) #define EZ_ARG_AT15(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, ...) \ EZ_ARG_HEAD(__VA_ARGS__) #define EZ_ARG_AT16(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, ...) \ EZ_ARG_HEAD(__VA_ARGS__) #define EZ_ARG_AT17(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, ...) \ EZ_ARG_HEAD(__VA_ARGS__) #define EZ_ARG_AT18(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, ...) \ EZ_ARG_HEAD(__VA_ARGS__) #define EZ_ARG_AT19(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, ...) \ EZ_ARG_HEAD(__VA_ARGS__) #define EZ_ARG_AT20(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, ...) \ EZ_ARG_HEAD(__VA_ARGS__) |
EZ_ARG_COUNT
是对_EZ_ARG_COUNT
的一个包装。它会被展开为
1 2 |
EZ_ARG_AT(20,##__VA_ARGS__, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) |
根据
1 2 3 |
#define EZ_ARG_AT(N, ...) EZ_ARG_AT_(N, __VA_ARGS__) #define EZ_ARG_AT_(N, ...) EZ_CONCAT(EZ_ARG_AT, N)(__VA_ARGS__) |
上面这个宏就是EZ_CONCAT(EZ_ARG_AT, 20)(__VA_ARGS__)
,而EZ_CONCAT(EZ_ARG_AT, 20)
也就是EZ_ARG_AT20
,因此这个宏进而就等同于
1 2 |
EZ_ARG_AT20(##__VA_ARGS__, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) |
也就是说我们在使用EZ_ARG_COUNT(...)
这个宏的时候,它会被最终展开为
1 2 |
EZ_ARG_AT20(##__VA_ARGS__, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) |
现在假设有这么一行代码EZ_ARG_COUNT(1,2,3)
,那么它就会展开为
1 2 |
EZ_ARG_AT20(1, 2, 3, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) |
我们注意到
1 2 3 |
#define EZ_ARG_AT20(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, ...) \ EZ_ARG_HEAD(__VA_ARGS__) |
1占据了_0的位置,2占据了_1的位置,3占据了_3的位置,20,19,18,…,4依次占据了
_4、_5、_6、… _19的位置,剩下的3,2,1,0被当做参数传入了 EZ_ARG_HEAD
中,
因此EZ_ARG_COUNT(1,2,3)
会被展开为 EZ_ARG_HEAD(3,2,1,0)
。
EZ_ARG_HEAD
EZ_ARG_HEAD
的定义如下
1 2 |
#define EZ_ARG_HEAD(FIRST, ...) FIRST |
它的作用是取出宏的第一个参数,因此
1 2 |
EZ_ARG_HEAD(3,2,1,0) = 3 |
也就是
1 2 |
EZ_ARG_COUNT(1,2,3) = 3 |
因此EZ_ARG_COUNT
的作用就是获得输入的参数的个数。
由以上三个宏的定义,EZTuple(@1, @"string")
会被展开为EZTupleAs(EZTuple2, @1, @"string")
。
进而,根据EZTupleAs
的定义
1 2 |
#define EZTupleAs(_Class_, ...) [[_Class_ alloc] EZ_CONCAT(initWith, EZ_FOR_EACH(EZ_INIT_PARAM_CALL, ,__VA_ARGS__))] |
EZTupleAs(EZTuple2, @1, @"string")
会被展开为
1 2 |
[[EZTuple2 alloc] EZ_CONCAT(initWith, EZ_FOR_EACH(EZ_INIT_PARAM_CALL, ,@1, @"string"))] |
EZ_FOR_EACH
EZ_FOR_EACH
的定义如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
#define EZ_FOR_EACH(...) _EZ_FOR_EACH(__VA_ARGS__) #define _EZ_FOR_EACH(MACRO, SEP, ...) EZ_FOR_EACH_CTX(EZ_FOR_EACH_ITER_, SEP, MACRO, ##__VA_ARGS__) #define EZ_FOR_EACH_CTX(MACRO, SEP, CTX, ...) EZ_CONCAT(EZ_FOR_EACH_CTX, EZ_ARG_COUNT(__VA_ARGS__))(MACRO, SEP, CTX, ##__VA_ARGS__) #define EZ_FOR_EACH_CTX0(MACRO, SEP, CTX) #define EZ_FOR_EACH_CTX1(MACRO, SEP, CTX, _0) MACRO(0, _0, CTX) #define EZ_FOR_EACH_CTX2(MACRO, SEP, CTX, _0, _1) \ EZ_FOR_EACH_CTX1(MACRO, SEP, CTX, _0) SEP MACRO(1, _1, CTX) #define EZ_FOR_EACH_CTX3(MACRO, SEP, CTX, _0, _1, _2) \ EZ_FOR_EACH_CTX2(MACRO, SEP, CTX, _0, _1) SEP MACRO(2, _2, CTX) #define EZ_FOR_EACH_CTX4(MACRO, SEP, CTX, _0, _1, _2, _3) \ EZ_FOR_EACH_CTX3(MACRO, SEP, CTX, _0, _1, _2) SEP MACRO(3, _3, CTX) #define EZ_FOR_EACH_CTX5(MACRO, SEP, CTX, _0, _1, _2, _3, _4) \ EZ_FOR_EACH_CTX4(MACRO, SEP, CTX, _0, _1, _2, _3) SEP MACRO(4, _4, CTX) // 中间定义都是类似的,为了节省篇幅就不列出了 EZ_FOR_EACH_CTX18(MACRO, SEP, CTX, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17) SEP MACRO(18, _18, CTX) #define EZ_FOR_EACH_CTX20(MACRO, SEP, CTX, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19) \ EZ_FOR_EACH_CTX19(MACRO, SEP, CTX, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18) SEP MACRO(19, _19, CTX) |
对于EZTupleAs(EZTuple2, @1, @"string")
展开的结果中的EZ_FOR_EACH(EZ_INIT_PARAM_CALL, ,@1, @"string")
而言,MACRO
为EZ_INIT_PARAM_CALL
,SEP
为空,...
为@1, @"string"
,所以
它会被展开为
1 2 |
EZ_FOR_EACH_CTX(EZ_FOR_EACH_ITER_, , EZ_INIT_PARAM_CALL, @1, @"string") |
根据
1 2 |
#define EZ_FOR_EACH_CTX(MACRO, SEP, CTX, ...) EZ_CONCAT(EZ_FOR_EACH_CTX, EZ_ARG_COUNT(__VA_ARGS__))(MACRO, SEP, CTX, ##__VA_ARGS__) |
那么对于EZ_FOR_EACH_CTX(EZ_FOR_EACH_ITER_, , EZ_INIT_PARAM_CALL, @1, @"string")
,MACRO
为EZ_FOR_EACH_ITER_
,SEP
为空,CTX
为EZ_INIT_PARAM_CALL
,所以它会被展开为
1 2 |
EZ_FOR_EACH_CTX2(EZ_FOR_EACH_ITER_,,EZ_INIT_PARAM_CALL,@1, @"string") |
再根据
1 2 3 4 |
#define EZ_FOR_EACH_CTX1(MACRO, SEP, CTX, _0) MACRO(0, _0, CTX) #define EZ_FOR_EACH_CTX2(MACRO, SEP, CTX, _0, _1) \ EZ_FOR_EACH_CTX1(MACRO, SEP, CTX, _0) SEP MACRO(1, _1, CTX) |
MACRO
为EZ_FOR_EACH_ITER_
,SEP
为空,CTX
为EZ_INIT_PARAM_CALL
,_0为@1,_1为@”string”,所以上式首先会被展开为
1 2 |
EZ_FOR_EACH_CTX1(EZ_FOR_EACH_ITER_, , EZ_INIT_PARAM_CALL, @1) EZ_FOR_EACH_ITER_(1, @"string", EZ_INIT_PARAM_CALL) |
然后
1 2 |
EZ_FOR_EACH_CTX1(EZ_FOR_EACH_ITER_, ,EZ_INIT_PARAM_CALL, @1) |
中,MACRO
为EZ_FOR_EACH_ITER_
,SEP
为空,CTX
为EZ_INIT_PARAM_CALL
,_0为@1,所以它会被展开成
1 2 |
EZ_FOR_EACH_ITER_(0,@1,EZ_INIT_PARAM_CALL) |
所以
1 2 |
EZ_FOR_EACH_CTX2(EZ_FOR_EACH_ITER_,,EZ_INIT_PARAM_CALL,@1, @"string") |
会被展开为
1 2 |
EZ_FOR_EACH_ITER_(0, @1, EZ_INIT_PARAM_CALL) EZ_FOR_EACH_ITER_(1, @"string", EZ_INIT_PARAM_CALL) |
也就是说,最一开始的
1 2 |
EZ_FOR_EACH(EZ_INIT_PARAM_CALL, ,@1, @"string") |
被展开为
1 2 |
EZ_FOR_EACH_ITER_(0, @1, EZ_INIT_PARAM_CALL) EZ_FOR_EACH_ITER_(1, @"string", EZ_INIT_PARAM_CALL) |
我们先关注EZ_FOR_EACH_ITER_(0, @1, EZ_INIT_PARAM_CALL)
的展开情况。EZ_FOR_EACH_ITER_(1, @"string", EZ_INIT_PARAM_CALL)
和它是类似的。
根据
1 2 3 4 5 |
#define EZ_FOR_EACH_ITER_(INDEX, PARAM, MACRO) MACRO(INDEX, PARAM) #define _EZ_INIT_PARAM_CALL_FIRST(index, param) EZ_ORDINAL_CAP_AT(index):param #define _EZ_INIT_PARAM_CALL(index, param) EZ_ORDINAL_AT(index):param #define EZ_INIT_PARAM_CALL(index, param) EZ_IF_EQ(0, index)(_EZ_INIT_PARAM_CALL_FIRST(index, param))(_EZ_INIT_PARAM_CALL(index, param)) |
那么EZ_FOR_EACH_ITER_(0, @1, EZ_INIT_PARAM_CALL)
会被展开为EZ_INIT_PARAM_CALL(0, @1)
,进而被展开为
1 2 |
EZ_IF_EQ(0, 0)(_EZ_INIT_PARAM_CALL_FIRST(0, @1))(_EZ_INIT_PARAM_CALL(0, @1)) |
EZ_IF_EQ
这个宏的定义如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
#define EZ_IF_EQ(A, B) EZ_CONCAT(EZ_IF_EQ, A)(B) #define EZ_CONSUME_(...) #define EZ_EXPAND_(...) __VA_ARGS__ #define EZ_IF_EQ0(VALUE) EZ_CONCAT(EZ_IF_EQ0_, VALUE) #define EZ_IF_EQ0_0(...) __VA_ARGS__ EZ_CONSUME_ #define EZ_IF_EQ0_1(...) EZ_EXPAND_ #define EZ_IF_EQ0_2(...) EZ_EXPAND_ #define EZ_IF_EQ0_3(...) EZ_EXPAND_ #define EZ_IF_EQ0_4(...) EZ_EXPAND_ #define EZ_IF_EQ0_5(...) EZ_EXPAND_ // 中间定义都是类似的,为了节省篇幅就不列出了 #define EZ_IF_EQ0_18(...) EZ_EXPAND_ #define EZ_IF_EQ0_19(...) EZ_EXPAND_ #define EZ_IF_EQ0_20(...) EZ_EXPAND_ #define EZ_IF_EQ1(VALUE) EZ_IF_EQ0(EZ_DEC(VALUE)) #define EZ_IF_EQ2(VALUE) EZ_IF_EQ1(EZ_DEC(VALUE)) #define EZ_IF_EQ3(VALUE) EZ_IF_EQ2(EZ_DEC(VALUE)) #define EZ_IF_EQ4(VALUE) EZ_IF_EQ3(EZ_DEC(VALUE)) #define EZ_IF_EQ5(VALUE) EZ_IF_EQ4(EZ_DEC(VALUE)) // 中间定义都是类似的,为了节省篇幅就不列出了 #define EZ_IF_EQ18(VALUE) EZ_IF_EQ17(EZ_DEC(VALUE)) #define EZ_IF_EQ19(VALUE) EZ_IF_EQ18(EZ_DEC(VALUE)) #define EZ_IF_EQ20(VALUE) EZ_IF_EQ19(EZ_DEC(VALUE)) |
那么EZ_IF_EQ(0, 0)
会被展开为EZ_IF_EQ0(0)
,EZ_IF_EQ0(0)
会被展开为EZ_IF_EQ0_0
,所以
1 2 |
EZ_IF_EQ(0, 0)(_EZ_INIT_PARAM_CALL_FIRST(0, @1))(_EZ_INIT_PARAM_CALL(0, @1)) |
会被展开为
1 2 |
EZ_IF_EQ0_0(_EZ_INIT_PARAM_CALL_FIRST(0, @1))(_EZ_INIT_PARAM_CALL(0, @1)) |
_EZ_INIT_PARAM_CALL_FIRST(0, @1)
会被展开为EZ_ORDINAL_CAP_AT(0):@1
,_EZ_INIT_PARAM_CALL(0, @1)
会被展开为EZ_ORDINAL_AT(0):@1
。
EZ_ORDINAL_CAP_AT
和EZ_ORDINAL_AT
的定义如下
1 2 3 4 5 |
#define EZ_ORDINAL_AT(N) EZ_ARG_AT(N, EZ_ORDINAL_NUMBERS) #define EZ_ORDINAL_CAP_AT(N) EZ_ARG_AT(N, EZ_ORDINAL_CAP_NUMBERS) #define EZ_ORDINAL_NUMBERS first, second, third, fourth, fifth, sixth, seventh, eighth, ninth, tenth, eleventh, twelfth, thirteenth, fourteenth, fifteenth, sixteenth, seventeenth, eighteenth, nineteenth, twentieth #define EZ_ORDINAL_CAP_NUMBERS First, Second, Third, Fourth, Fifth, Sixth, Seventh, Eighth, Ninth, Tenth, Eleventh, Twelfth, Thirteenth, Fourteenth, Fifteenth, Sixteenth, Seventeenth, Eighteenth, Nineteenth, Twentieth |
而 EZ_ARG_AT(N, EZ_ORDINAL_NUMBERS)
这个宏是取EZ_ORDINAL_NUMBERS
中的第 N 个参数,因此EZ_ORDINAL_CAP_AT(0):@1
也就是First:@1
,EZ_ORDINAL_AT(0):@1
也就是first:@1
。
所以
1 2 |
EZ_IF_EQ0_0(_EZ_INIT_PARAM_CALL_FIRST(0, @1))(_EZ_INIT_PARAM_CALL(0, @1)) |
也就是
1 2 |
EZ_IF_EQ0_0(First:@1)(first:@1) |
由
1 2 3 |
#define EZ_IF_EQ0_0(...) __VA_ARGS__ EZ_CONSUME_ #define EZ_CONSUME_(...) |
可知,
1 2 |
EZ_IF_EQ0_0(First:@1)(first:@1) |
会被展开为First:@1 EZ_CONSUME_(first:@1)
,进而展开为First:@1
。
同理
1 2 |
EZ_FOR_EACH_ITER_(1, @"string", EZ_INIT_PARAM_CALL) |
展开后得到Second:@"string"
。
那么,
1 2 |
EZ_FOR_EACH(EZ_INIT_PARAM_CALL, ,@1, @"string") |
最终被展开的结果就是First:@1 Second:@"string"
,所以
1 2 |
EZ_CONCAT(initWith, EZ_FOR_EACH(EZ_INIT_PARAM_CALL, ,@1, @"string")) |
展开的结果就是initWithFirst:@1 Second:@"string"
。
因此EZTupleAs(EZTuple2, @1, @"string")
就是
1 2 |
[[EZTuple2 alloc] initWithFirst:@1 Second:@"string"] |
所以最开始的EZTuple(@1,@"string")
就被转换为了 Objective-C 中的类的创建语法。
EZ_TUPLE_CLASSES_DEF
回到最开始的声明:
1 2 |
EZTuple2<NSNumber *, NSString *> *tuple = EZTuple(@1, @"string"); |
左边的EZTuple2
是一个类的名字,但是如果通过Xcode 中的Go To Definition
来查看这个类的定义的话,会发现 Xcode 将这个类的定义定位到了一个文件中,这个文件里除了头文件外只有一行宏定义:
1 2 3 4 5 |
// EZTupleSubClasses.h #import <Foundation/Foundation.h> #import <EasyTuple/EZMetaMacros.h> EZ_TUPLE_CLASSES_DEF |
EZ_TUPLE_CLASSES_DEF
这个宏的定义如下
1 2 |
#define EZ_TUPLE_CLASSES_DEF EZ_FOR(20, EZ_TUPLE_DEF_FOREACH, ;) |
而 EZ_FOR
的定义则是
1 2 3 4 5 6 7 8 9 10 11 |
#define EZ_FOR(COUNT, MARCO, SEP) EZ_CONCAT(EZ_FOR, COUNT)(MARCO, SEP) #define EZ_FOR0(MARCO, SEP) #define EZ_FOR1(MARCO, SEP) MARCO(0) #define EZ_FOR2(MARCO, SEP) EZ_FOR1(MARCO, SEP) SEP MARCO(1) #define EZ_FOR3(MARCO, SEP) EZ_FOR2(MARCO, SEP) SEP MARCO(2) #define EZ_FOR4(MARCO, SEP) EZ_FOR3(MARCO, SEP) SEP MARCO(3) #define EZ_FOR5(MARCO, SEP) EZ_FOR4(MARCO, SEP) SEP MARCO(4) // 中间定义都是类似的,为了节省篇幅就不列出了 #define EZ_FOR19(MARCO, SEP) EZ_FOR18(MARCO, SEP) SEP MARCO(18) #define EZ_FOR20(MARCO, SEP) EZ_FOR19(MARCO, SEP) SEP MARCO(19) |
对于EZ_FOR(20, EZ_TUPLE_DEF_FOREACH, ;)
,COUNT
为20,MACRO
为EZ_TUPLE_DEF_FOREACH
,SEP
为;
,因此它会被展开为EZ_CONCAT(EZ_FOR, COUNT)(MARCO, SEP)
,即EZ_FOR20(EZ_TUPLE_DEF_FOREACH,;)
。而EZ_FOR20(EZ_TUPLE_DEF_FOREACH,;)
展开后得到
1 2 |
EZ_FOR19(EZ_TUPLE_DEF_FOREACH,;) ; EZ_TUPLE_DEF_FOREACH(19) |
EZ_FOR19(EZ_TUPLE_DEF_FOREACH,;)
展开后得到
1 2 |
EZ_FOR18(EZ_TUPLE_DEF_FOREACH,;) ; EZ_TUPLE_DEF_FOREACH(18) ; EZ_TUPLE_DEF_FOREACH(18) |
这样一层层展开,最终的结果为
1 2 |
EZ_TUPLE_DEF_FOREACH(0) ; EZ_TUPLE_DEF_FOREACH(1) ; EZ_TUPLE_DEF_FOREACH(2) ; ... EZ_TUPLE_DEF_FOREACH(18); EZ_TUPLE_DEF_FOREACH(19) |
EZ_TUPLE_DEF_FOREACH(index)
的定义如下
1 2 |
#define EZ_TUPLE_DEF_FOREACH(index) EZ_TUPLE_DEF(EZ_INC(index)) |
内层的EZ_INC(index)
会对index进行加1操作,那么EZ_TUPLE_CLASSES_DEF
就会展开为
1 2 |
EZ_TUPLE_DEF(1) ; EZ_TUPLE_DEF(2) ; ... EZ_TUPLE_DEF(18) ; EZ_TUPLE_DEF(19) ; EZ_TUPLE_DEF(20) |
接下来我们再看一下 EZ_TUPLE_DEF(i)
的定义:
1 2 3 4 5 6 7 8 9 10 11 |
#define EZ_TUPLE_DEF(i) \ @interface EZ_CONCAT(EZTuple, i)<EZ_FOR_COMMA(i, EZ_GENERIC_TYPE)> :EZTupleBase \ \ EZ_FOR_RECURSIVE(i, EZ_PROPERTY_DEF, ;); \ \ @property (nonatomic, strong) EZ_CHARS_AT(EZ_DEC(i)) last; \ \ - (instancetype)EZ_CONCAT(initWith, EZ_FOR_SPACE(i, EZ_INIT_PARAM)); \ \ @end |
可以看出EZ_TUPLE_DEF(i)
展开后是一个类的定义,并且这个类的定义明显地可以分为三部分:第一部分是拼接出来的类名,该类继承自EZTupleBase
,第二部分是通过EZ_FOR_RECURSIVE
生成的属性,第三部分是拼接出来的初始化方法。
EZ_FOR_COMMA
这个宏出现在类名中,
1 2 3 4 5 6 7 8 9 10 11 12 |
#define EZ_FOR_COMMA(COUNT, MARCO) EZ_CONCAT(EZ_FOR_COMMA, COUNT)(MARCO) #define EZ_FOR_COMMA0(MARCO) #define EZ_FOR_COMMA1(MARCO) MARCO(0) #define EZ_FOR_COMMA2(MARCO) EZ_FOR_COMMA1(MARCO), MARCO(1) #define EZ_FOR_COMMA3(MARCO) EZ_FOR_COMMA2(MARCO), MARCO(2) #define EZ_FOR_COMMA4(MARCO) EZ_FOR_COMMA3(MARCO), MARCO(3) #define EZ_FOR_COMMA5(MARCO) EZ_FOR_COMMA4(MARCO), MARCO(4) // 中间定义都是类似的,为了节省篇幅就不列出了 #define EZ_FOR_COMMA18(MARCO) EZ_FOR_COMMA17(MARCO), MARCO(17) #define EZ_FOR_COMMA19(MARCO) EZ_FOR_COMMA18(MARCO), MARCO(18) #define EZ_FOR_COMMA20(MARCO) EZ_FOR_COMMA19(MARCO), MARCO(19) |
这个宏和上面提过的EZ_FOR
非常类似,所以展开的结果也是类似的。那么对于EZ_FOR_COMMA(2, EZ_GENERIC_TYPE)
,展开的结果为
1 2 |
EZ_GENERIC_TYPE(0), EZ_GENERIC_TYPE(1) |
EZ_GENERIC_TYPE(index)
1 2 3 4 5 6 |
#define EZ_GENERIC_TYPE(index) __covariant EZ_CHARS_AT(index): id #define EZ_CHARS_AT(N) EZ_ARG_AT(N, EZ_CHARS) #define EZ_CHARS A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z |
EZ_CHARS_AT(N)
的作用是从EZ_CHARS
取出第 N 位的字符,因此EZ_GENERIC_TYPE(0)
会被展开为__covariant A: id
,EZ_GENERIC_TYPE(1)
会被展开为__covariant B: id
,
所以
1 2 |
EZ_FOR_COMMA(i, EZ_GENERIC_TYPE) |
会被展开为
1 2 |
__covariant A: id, __covariant B: id |
__covariant
这个关键字表示协变性,即子类型可以强转到父类型,是Objective-C 新出现的一个用来表达泛型能力的关键字,与它一同出现的另一个关键字是____contravariant
,表示逆变性,即父类型可以强转到子类型。对这两个关键字的更详细介绍,可以看一下 sunnyxx 老师的博文《2015 Objective-C 新特性》。
回到类的接口定义,根据上面的分析,
1 2 |
@interface EZ_CONCAT(EZTuple, i)<EZ_FOR_COMMA(i, EZ_GENERIC_TYPE)> :EZTupleBase |
会被展开为
1 2 |
@interface EZTuple2<__covariant A: id, __covariant B: id)> :EZTupleBase |
EZ_FOR_RECURSIVE
这个宏被用于类定义的第二部分,即类的属性的生成中。它的定义和EZ_FOR
也是类似的:
1 2 3 4 5 6 7 8 9 10 11 |
#define EZ_FOR_RECURSIVE(COUNT, MARCO, SEP) EZ_CONCAT(EZ_FOR_RECURSIVE, COUNT)(MARCO, SEP) #define EZ_FOR_RECURSIVE0(MARCO, SEP) #define EZ_FOR_RECURSIVE1(MARCO, SEP) MARCO(0) #define EZ_FOR_RECURSIVE2(MARCO, SEP) EZ_FOR_RECURSIVE1(MARCO, SEP) SEP MARCO(1) #define EZ_FOR_RECURSIVE3(MARCO, SEP) EZ_FOR_RECURSIVE2(MARCO, SEP) SEP MARCO(2) #define EZ_FOR_RECURSIVE4(MARCO, SEP) EZ_FOR_RECURSIVE3(MARCO, SEP) SEP MARCO(3) #define EZ_FOR_RECURSIVE5(MARCO, SEP) EZ_FOR_RECURSIVE4(MARCO, SEP) SEP MARCO(4) // 中间定义都是类似的,为了节省篇幅就不列出了 #define EZ_FOR_RECURSIVE19(MARCO, SEP) EZ_FOR_RECURSIVE18(MARCO, SEP) SEP MARCO(18) #define EZ_FOR_RECURSIVE20(MARCO, SEP) EZ_FOR_RECURSIVE19(MARCO, SEP) SEP MARCO(19) |
所以
1 2 |
EZ_FOR_RECURSIVE(2, EZ_PROPERTY_DEF, ;) |
就会被展开为
1 2 |
EZ_PROPERTY_DEF(0) ; EZ_PROPERTY_DEF(1) |
EZ_PROPERTY_DEF(index)
1 2 3 4 5 6 |
#define EZ_PROPERTY_DEF(index) @property (nonatomic, strong) EZ_CHARS_AT(index) EZ_ORDINAL_AT(index) #define EZ_ORDINAL_NUMBERS first, second, third, fourth, fifth, sixth, seventh, eighth, ninth, tenth, eleventh, twelfth, thirteenth, fourteenth, fifteenth, sixteenth, seventeenth, eighteenth, nineteenth, twentieth #define EZ_ORDINAL_AT(N) EZ_ARG_AT(N, EZ_ORDINAL_NUMBERS) |
所以
1 2 |
EZ_FOR_RECURSIVE(2, EZ_PROPERTY_DEF, ;) |
首先被展开为
1 2 |
EZ_PROPERTY_DEF(0) ; EZ_PROPERTY_DEF(1) |
进而被展开为
1 2 3 |
@property (nonatomic, strong) A first; @property (nonatomic, strong) B second; |
EZ_FOR_SPACE
这个宏的名字和EZ_FOR_COMMA
类似,定义也类似,因此它的作用也是类似的,这里就不浪费篇幅了。EZ_FOR_SPACE(2, EZ_INIT_PARAM)
会被展开为
1 2 |
EZ_INIT_PARAM(0) EZ_INIT_PARAM(1) |
这里我们也只看一下EZ_INIT_PARAM(0)
是如何展开的,EZ_INIT_PARAM(1)
的展开和它是类似的。
1 2 3 4 |
#define _EZ_INIT_PARAM_FIRST(index) EZ_ORDINAL_CAP_AT(index):(EZ_CHARS_AT(index))EZ_ORDINAL_AT(index) #define _EZ_INIT_PARAM(index) EZ_ORDINAL_AT(index):(EZ_CHARS_AT(index))EZ_ORDINAL_AT(index) #define EZ_INIT_PARAM(index) EZ_IF_EQ(0, index)(_EZ_INIT_PARAM_FIRST(index))(_EZ_INIT_PARAM(index)) |
EZ_INIT_PARAM(0)
首先被展开为
1 2 |
EZ_IF_EQ(0,0)(_EZ_INIT_PARAM_FIRST(0))(_EZ_INIT_PARAM(0)) |
_EZ_INIT_PARAM_FIRST(0)
会被展开为
1 2 |
First:(A)first |
而_EZ_INIT_PARAM(0)
会被展开为
1 2 |
first:(A)first |
所以
1 2 |
EZ_IF_EQ(0,0)(_EZ_INIT_PARAM_FIRST(0))(_EZ_INIT_PARAM(0)) |
也就是
1 2 |
EZ_IF_EQ(0,0)(First:(A)first)(first:(A)first) |
EZ_IF_EQ(0,0)
也就是EZ_IF_EQ0(0)
,也就是EZ_IF_EQ0_0
,
根据
1 2 |
#define EZ_IF_EQ0_0(...) __VA_ARGS__ EZ_CONSUME_ |
EZ_IF_EQ(0,0)(First:(A)first)(first:(A)first)
就是
1 2 |
First:(A)first EZ_CONSUME_(first:(A)first) |
最终就是First:(A)first
。即EZ_INIT_PARAM(0)
最终展开的结果就是First:(A)first
。同理,EZ_INIT_PARAM(1)
最终展开的结果就是Second:(B)second
。
因此,
1 2 |
- (instancetype)EZ_CONCAT(initWith, EZ_FOR_SPACE(i, EZ_INIT_PARAM)); |
展开后的结果就是
1 2 |
- (instancetype)initWithFirst:(A)first Second:(B)second; |
回到一开始的类定义
1 2 3 4 5 6 7 8 9 10 11 |
#define EZ_TUPLE_DEF(i) \ @interface EZ_CONCAT(EZTuple, i)<EZ_FOR_COMMA(i, EZ_GENERIC_TYPE)> :EZTupleBase \ \ EZ_FOR_RECURSIVE(i, EZ_PROPERTY_DEF, ;); \ \ @property (nonatomic, strong) EZ_CHARS_AT(EZ_DEC(i)) last; \ \ - (instancetype)EZ_CONCAT(initWith, EZ_FOR_SPACE(i, EZ_INIT_PARAM)); \ \ @end |
当 i = 2的时候,上面这段宏就会被展开为
1 2 3 4 5 6 7 8 9 10 |
@interface EZTuple2<__covariant A: id, __covariant B: id)> :EZTupleBase @property (nonatomic, strong) A first; @property (nonatomic, strong) B second; @property (nonatomic, strong) B last; - (instancetype)initWithFirst:(A)first Second:(B)second; @end |
我们在前面分析过,EZ_TUPLE_CLASSES_DEF
会被展开为
1 2 |
EZ_TUPLE_DEF(1) ; EZ_TUPLE_DEF(2) ; ... EZ_TUPLE_DEF(18) ; EZ_TUPLE_DEF(19) ; EZ_TUPLE_DEF(20) |
所以在预编译时,这些宏就会被展开成EZTuple1
、EZTuple2
……EZTuple20
等类的定义。也就是说,EasyTuple
通过这个宏为我们一口气定义了20个元祖类。这样的好处是显而易见的:如果不使用宏的话,那么为了创建这么多个类,我们就要手动去书写很多重复的代码;而使用宏的话,则能够通过宏的巧妙组合在预编译的时候自动生成代码。
小结
宏是一门非常非常强大的技术,但是之前我一直不知道它有这么多高级的玩法和用法,看了EasyTuple
的源代码,真是让人大开眼界。其实EasyTuple
中还有一些上文中没有提到的宏的用法,限于篇幅这里就不一一展开分析了。