2 篇文章 0 订阅
4 篇文章 0 订阅

宏定义的黑魔法 - 宏菜鸟起飞手册

入门

//This defines PI
#define M_PI        3.14159265358979323846264338327950288


#define关键字表明即将开始定义一个宏，紧接着的M_PI是宏的名字，空格之后的数字是内容。类似这样的#define X A的宏是比较简单的，在编译时编译器会在语义分析认定是宏后，将X替换为A，这个过程称为宏的展开。比如对于上面的M_PI

#define M_PI        3.14159265358979323846264338327950288

double r = 10.0;
double circlePerimeter = 2 * M_PI * r;
// => double circlePerimeter = 2 * 3.14159265358979323846264338327950288 * r;

printf("Pi is %0.7f",M_PI);
//Pi is 3.1415927


//A simple function-like macro
#define SELF(x)      x
NSString *name = @"Macro Rookie";
NSLog(@"Hello %@",SELF(name));
// => NSLog(@"Hello %@",name);
//   => Hello Macro Rookie


#define PLUS(x,y) x + y
printf("%d",PLUS(3,2));
// => printf("%d",3 + 2);
//  => 5


宏的世界，小有乾坤

//Version 1.0
#define MIN(A,B) A < B ? A : B


Try一下

int a = MIN(1,2);
// => int a = 1 < 2 ? 1 : 2;
printf("%d",a);
// => 1


int a = 2 * MIN(3, 4);
printf("%d",a);
// => 4


int a = 2 * MIN(3, 4);
// => int a = 2 * 3 < 4 ? 3 : 4;
// => int a = 6 < 4 ? 3 : 4;
// => int a = 4;


//Version 2.0
#define MIN(A,B) (A < B ? A : B)


int a = MIN(3, 4 < 5 ? 4 : 5);
printf("%d",a);
// => 4


int a = MIN(3, 4 < 5 ? 4 : 5);
// => int a = (3 < 4 < 5 ? 4 : 5 ? 3 : 4 < 5 ? 4 : 5);  //希望你还记得运算符优先级
//  => int a = ((3 < (4 < 5 ? 4 : 5) ? 3 : 4) < 5 ? 4 : 5);  //为了您不太纠结，我给这个式子加上了括号
//   => int a = ((3 < 4 ? 3 : 4) < 5 ? 4 : 5)
//    => int a = (3 < 5 ? 4 : 5)
//     => int a = 4


//Version 3.0
#define MIN(A,B) ((A) < (B) ? (A) : (B))


float a = 1.0f;
float b = MIN(a++, 1.5f);
printf("a=%f, b=%f",a,b);
// => a=3.000000, b=2.000000


float a = 1.0f;
float b = MIN(a++, 1.5f);
// => float b = ((a++) < (1.5f) ? (a++) : (1.5f))


int a = ({
int b = 1;
int c = 2;
b + c;
});
// => a is 3


//GNUC MIN
#define MIN(A,B)	({ __typeof__(A) __a = (A); __typeof__(B) __b = (B); __a < __b ? __a : __b; })


//CLANG MIN
#define __NSX_PASTE__(A,B) A##B

#define MIN(A,B) __NSMIN_IMPL__(A,B,__COUNTER__)

#define __NSMIN_IMPL__(A,B,L) ({ __typeof__(A) __NSX_PASTE__(__a,L) = (A); __typeof__(B) __NSX_PASTE__(__b,L) = (B); (__NSX_PASTE__(__a,L) < __NSX_PASTE__(__b,L)) ? __NSX_PASTE__(__a,L) : __NSX_PASTE__(__b,L); })


#define __NSX_PASTE__(A,B) A##B

#define MIN(A,B) __NSMIN_IMPL__(A,B,__COUNTER__)

#define __NSMIN_IMPL__(A,B,L) ({ __typeof__(A) __NSX_PASTE__(__a,L) = (A); \
__typeof__(B) __NSX_PASTE__(__b,L) = (B); \
(__NSX_PASTE__(__a,L) < __NSX_PASTE__(__b,L)) ? __NSX_PASTE__(__a,L) : __NSX_PASTE__(__b,L); \
})


Log，永恒的主题

Log人人爱，它为我们指明前进方向，它为我们抓虫提供帮助。在objc中，我们最多使用的log方法就是NSLog输出信息到控制台了，但是NSLog的标准输出可谓残废，有用信息完全不够，比如下面这段代码：

NSArray *array = @[@"Hello", @"My", @"Macro"];
NSLog (@"The array is %@", array);


2014-01-20 11:22:11.835 TestProject[23061:70b] The array is (
Hello,
My,
Macro
)


//A better version of NSLog
#define NSLog(format, ...) do {                                                                          \
fprintf(stderr, "<%s : %d> %s\n",                                           \
[[[NSString stringWithUTF8String:__FILE__] lastPathComponent] UTF8String],  \
__LINE__, __func__);                                                        \
(NSLog)((format), ##__VA_ARGS__);                                           \
fprintf(stderr, "-------\n");                                               \
} while (0)


//A wrong version of NSLog
#define NSLog(format, ...)   fprintf(stderr, "<%s : %d> %s\n",                                           \
[[[NSString stringWithUTF8String:__FILE__] lastPathComponent] UTF8String],  \
__LINE__, __func__);                                                        \
(NSLog)((format), ##__VA_ARGS__);                                           \
fprintf(stderr, "-------\n");


if (errorHappend)
NSLog(@"Oops, error happened");


if (errorHappend)
fprintf((stderr, "<%s : %d> %s\n",[[[NSString stringWithUTF8String:__FILE__] lastPathComponent] UTF8String], __LINE__, __func__);
(NSLog)((format), ##__VA_ARGS__); //I will expand this later
fprintf(stderr, "-------\n");


//Another wrong version of NSLog
#define NSLog(format, ...)   {
fprintf(stderr, "<%s : %d> %s\n",                                           \
[[[NSString stringWithUTF8String:__FILE__] lastPathComponent] UTF8String],  \
__LINE__, __func__);                                                        \
(NSLog)((format), ##__VA_ARGS__);                                           \
fprintf(stderr, "-------\n");                                               \
}


//I am sorry if you don't like { in the same like. But I am a fan of this style :P
if (errorHappend) {
fprintf((stderr, "<%s : %d> %s\n",[[[NSString stringWithUTF8String:__FILE__] lastPathComponent] UTF8String], __LINE__, __func__);
(NSLog)((format), ##__VA_ARGS__);
fprintf(stderr, "-------\n");
};


if (errorHappend)
NSLog(@"Oops, error happened");
else
//Yep, no error, I am happy~ :)


No! I am not haapy at all! 因为编译错误了！实际上这个宏展开以后变成了这个样子：

if (errorHappend) {
fprintf((stderr, "<%s : %d> %s\n",[[[NSString stringWithUTF8String:__FILE__] lastPathComponent] UTF8String], __LINE__, __func__);
(NSLog)((format), ##__VA_ARGS__);
fprintf(stderr, "-------\n");
}; else {
//Yep, no error, I am happy~ :)
}


if (errorHappend)
do {
fprintf((stderr, "<%s : %d> %s\n",[[[NSString stringWithUTF8String:__FILE__] lastPathComponent] UTF8String], __LINE__, __func__);
(NSLog)((format), ##__VA_ARGS__);
fprintf(stderr, "-------\n");
} while (0);
else {
//Yep, no error, I am really happy~ :)
}


[[[NSString stringWithUTF8String:__FILE__] lastPathComponent] UTF8String], __LINE__, __func__);


-------
<AppDelegate.m : 46> -[AppDelegate application:didFinishLaunchingWithOptions:]
2014-01-20 16:44:25.480 TestProject[30466:70b] The array is (
Hello,
My,
Macro
)
-------


NSLog(@"Hello");
=> do {
fprintf((stderr, "<%s : %d> %s\n",[[[NSString stringWithUTF8String:__FILE__] lastPathComponent] UTF8String], __LINE__, __func__);
(NSLog)((@"Hello"), );
fprintf(stderr, "-------\n");
} while (0);



#define NSLogRect(rect) NSLog(@"%s x:%.4f, y:%.4f, w:%.4f, h:%.4f", #rect, rect.origin.x, rect.origin.y, rect.size.width, rect.size.height)
#define NSLogSize(size) NSLog(@"%s w:%.4f, h:%.4f", #size, size.width, size.height)
#define NSLogPoint(point) NSLog(@"%s x:%.4f, y:%.4f", #point, point.x, point.y)


两个实际应用的例子

#define XCTAssertTrue(expression, format...) \
_XCTPrimitiveAssertTrue(expression, ## format)

#define _XCTPrimitiveAssertTrue(expression, format...) \
({ \
@try { \
BOOL _evaluatedExpression = !!(expression); \
if (!_evaluatedExpression) { \
_XCTRegisterFailure(_XCTFailureDescription(_XCTAssertion_True, 0, @#expression),format); \
} \
} \
@catch (id exception) { \
_XCTRegisterFailure(_XCTFailureDescription(_XCTAssertion_True, 1, @#expression, [exception reason]),format); \
}\
})


//调用 RACSignal是类的名字
RACSignal *signal = RACObserve(self, currentLocation);

//以下开始是宏定义
//rac_valuesForKeyPath:observer:是方法名
#define RACObserve(TARGET, KEYPATH) \
[(id)(TARGET) rac_valuesForKeyPath:@keypath(TARGET, KEYPATH) observer:self]

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

//这个宏在取得keypath的同时在编译期间判断keypath是否存在，避免误写
//您可以先不用介意这里面的巫术..
#define keypath1(PATH) \
(((void)(NO && ((void)PATH, NO)), strchr(# PATH, '.') + 1))

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

//A和B是否相等，若相等则展开为后面的第一项，否则展开为后面的第二项
//eg. metamacro_if_eq(0, 0)(true)(false) => true
//    metamacro_if_eq(0, 1)(true)(false) => false
#define metamacro_if_eq(A, B) \
metamacro_concat(metamacro_if_eq, A)(B)

#define metamacro_if_eq1(VALUE) metamacro_if_eq0(metamacro_dec(VALUE))

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

#define metamacro_if_eq0_1(...) metamacro_expand_

#define metamacro_expand_(...) __VA_ARGS__

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

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

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

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

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

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

//调用 RACSignal是类的名字RACSignal *signal = RACObserve(self, currentLocation);
12-23 1135
08-21 14万+
06-28 1万+
07-21 277
01-23 1610
11-07 6万+
04-24 2172
09-25 443
06-01 1万+
04-02 193
01-22 1333

• 非常没帮助
• 没帮助
• 一般
• 有帮助
• 非常有帮助