在学习唱吧开源库的时候,发现关于日志打印的宏定义写的特别的好,可以决定每个类是否打印日志,以及是否保存到log文件中。具体如下:
/**
* Log Enable Config
*/
#define KTVHCLogEnable(target, console_log_enable, record_log_enable) \
static BOOL const KTVHCLog_##target##_ConsoleLogEnable = console_log_enable; \
static BOOL const KTVHCLog_##target##_RecordLogEnable = record_log_enable;
#define KTVHCLogEnableValueConsoleLog(target) KTVHCLog_##target##_ConsoleLogEnable
#define KTVHCLogEnableValueRecordLog(target) KTVHCLog_##target##_RecordLogEnable
其中,关于宏定义中 ##的使用,我在网上找了一下,详细的解释如下:
原文地址:https://github.com/awesome-tips/iOS-Tips/blob/master/2017/12.md
宏中的 ## 的含义
作者: Lefe_x
在宏的定义中,我们也许会遇到过 ##,比如下面是一些第三方库中 ## 使用场景:
微信 WCDB 中的宏定义: #define __WCDB_BINDING(className) _s_##className##_binding
唱吧 KTVHTTPCache 定义不同类中是否可以打印的例子: #define KTVHCLogEnableValueConsoleLog(target) KTVHCLog_##target##_ConsoleLogEnable
那 ## 有什么用呢? ## 在宏中的作用就是先分隔,然后进行强制连接。我们可能会定义不同的函数名或变量时就可以使用这样的宏定义。
那 ## 是如何工作的呢?
__WCDB_BINDING(className)
,首先 _s_##className##_binding
会拆分成 _s
,className
,_binding
。__WCDB_BINDING(ViewController)
将会被替换成 _s_ViewController_binding
;
KTVHCLogEnableValueConsoleLog(target)
,首先 KTVHCLog_##target##_ConsoleLogEnable
会被拆分为 KTVHCLog_, targe
t 和 _ConsoleLogEnable
。KTVHCLogEnableValueConsoleLog(Lefex)
会被替换成 KTVHCLog_ Lefex_ConsoleLogEnable
;
3.当使用 KTVHCLogEnable(HTTPServer, YES)
,将会定义一个名为 KTVHCLog_ HTTPServer_ConsoleLogEnable
静态常量,初始值为 YES。
#define KTVHCLogEnable(target, console_log_enable) \
static BOOL const KTVHCLog_##target##_ConsoleLogEnable = console_log_enable; \
比如我们使用不同的 View 名字创建不同的 View:
#define Name(target) weibo_##target##_name
#define View(target) view##target##Label
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSString * Name(lefex) = @"Lefe_x";
// 打印:You weibo name is: Lefe_x
NSLog(@"You weibo name is: %@", weibo_lefex_name);
UILabel *View(1) = [UILabel new];
view1Label.backgroundColor = [UIColor redColor];
UIView *View(2) = [UIView new];
view2Label.backgroundColor = [UIColor yellowColor];
}
是谁调了我的底层库
作者: Lefe_x
调试的时候,往往底层库会埋一些 NSLog 来调试,使用下面这种方式打印出来的函数名称 __PRETTY_FUNCTION__
是底层库的函数名。
# define LEFLog(fmt, ...) NSLog((@"%s (%d) => " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__)
打印是这样的:+[Network post]
中打印了 I am a log
,而不清楚是谁调用了。
+[Network post] (22) => I am a log
但是,我想要的是我在最顶层调用时的函数名,这样我可以很容易的看到是那个方法调用了底层库。
不太理解?举个例子吧: 每个 APP 都会有一个网络层,业务层会直接与网络层进行交互。调试的时候,我想知道 A 请求是在哪个页面中的哪个函数被调用了,咋么办?前提是 NSLog 在底层库。我们可以这样实现:
@implementation LEFLog
+ (NSString *)lastCallMethod
{
NSArray *symbols = [NSThread callStackSymbols];
NSInteger maxCount = symbols.count;
NSString *secondSymbol = maxCount > 2 ? symbols[2] : (maxCount > 1 ? symbols[1] : [symbols firstObject]);
if (secondSymbol.length == 0) {
return @"";
}
NSString *pattern = @"[+-]\\[.{0,}\\]";
NSError *error;
NSRegularExpression *express = [NSRegularExpression regularExpressionWithPattern:pattern options:kNilOptions error:&error];
if (error) {
NSLog(@"Error: %@", error);
return @"";
}
NSTextCheckingResult *checkResult = [[express matchesInString:secondSymbol options:NSMatchingReportCompletion range:NSMakeRange(0, secondSymbol.length)] lastObject];
NSString *findStr = [secondSymbol substringWithRange:checkResult.range];
return findStr ?: @"";
}
@end
然后定义一个宏:
# define LEFLog(fmt, ...) NSLog((@"%@, %s (%d) => " fmt), [LEFLog lastCallMethod], __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__
打印结果是这样的:在 LefexViewController 中的 viewDidLoad 调用了 Network 的 post 方法,并打印 I am a log.
-[LefexViewController viewDidLoad], +[Network post] (22) => I am a log
更多优质文章,可以微信扫码关注: