结论:
全局变量不可在.h中定义,要在.m中定义。 如果外界需要修改 可在.h中extern或者.h提供修改API才是正道。
踩坑背景:
功能要求:框架内部封装打印DebugLog信息,并向外提供控制LogEnable的接口。
XXLog.h
//暂时忽略多线程下调用加锁的问题
static BOOL kLoggingEnabled = NO;
#define XXDebug(fmt,...) XXLog(fmt, ## __VA_ARGS__);
static inline void XXLog(NSString *format, ...) {
//NSLog(@"%p = %@",&kLoggingEnabled,[NSString stringWithFormat:@"%d",kLoggingEnabled]);
if (!kLoggingEnabled) return;
va_list arg_list;
va_start (arg_list, format);
NSString *formattedString = [[NSString alloc] initWithFormat:format arguments:arg_list];
NSLog(@"[XXLog]: %@", formattedString);
va_end(arg_list);
}
XXKit.h
//向外提供了控制内部打印的接口
+ (void)setEnableLog:(BOOL)enableLog;
+ (BOOL)getEnableLog;
XXKit.m
#import “XXLog.h”
+ (void)setEnableLog:(BOOL)enableLog {
kLoggingEnabled = enableLog;
}
+ (BOOL)getEnableLog {
return kLoggingEnabled;
}
- (void)testA {
....
XXDebug(@"Log info!!!");
....
}
OtherFile.m
#import “XXLog.h”
- (void)otherFunc {
....
XXDebug(@"OtherFile Log info!!!");
....
}
结果现象:
当外界调用 [XXKit setEnableLog: ![XXKit getEnableLog]]; 仅仅能控制XXKit文件下的Log信息输出与否,而OtherFile.m下的XXDebug信息始终不输出!!
分析:
上述代码思路:定义一个全局BOOL变量和真实Log方法,在Log方法开始,判断BOOL值 enable = NO则return,而向外提供的接口只需更改这个全局变量的值即可。而且在.h中实现 简洁省去了.m 满足了强迫症和装逼心理 : )
但思考编译原理即可知晓: 当import 一个.h时,预编译会简单的把.h中内容Copy进来,所以当XXKit.m 和 OhterFile.m 分别import了XXLog.h后 编译后的结果为:
XXKit.m
//替换#import “XXLog.h”的结果
static BOOL kLoggingEnabled = NO;
#define XXDebug(fmt,...) XXLog(fmt, ## __VA_ARGS__);
static inline void XXLog(NSString *format, ...) {
//NSLog(@"%p = %@",&kLoggingEnabled,[NSString stringWithFormat:@"%d",kLoggingEnabled]);
if (!kLoggingEnabled) return;
va_list arg_list;
va_start (arg_list, format);
NSString *formattedString = [[NSString alloc] initWithFormat:format arguments:arg_list];
NSLog(@"[XXLog]: %@", formattedString);
va_end(arg_list);
}
......
OtherFile.m
//替换#import “XXLog.h”的结果
static BOOL kLoggingEnabled = NO;
#define XXDebug(fmt,...) XXLog(fmt, ## __VA_ARGS__);
static inline void XXLog(NSString *format, ...) {
//NSLog(@"%p = %@",&kLoggingEnabled,[NSString stringWithFormat:@"%d",kLoggingEnabled]);
if (!kLoggingEnabled) return;
va_list arg_list;
va_start (arg_list, format);
NSString *formattedString = [[NSString alloc] initWithFormat:format arguments:arg_list];
NSLog(@"[XXLog]: %@", formattedString);
va_end(arg_list);
}
......
这就会导致每一个import “XXLog.h”的.m中 都有一个 static BOOL kLoggingEnabled变量(static变量的作用域是一个编译单元即当前文件)所以当每个文件下调用XXDebug宏 即调用XXLog方法打印信息时,XXLog方法中的kLoggingEnabled变量都是当前文件下的static全局变量,而外界通过调用[XXKit setEnableLog:] 方法只更改了XXKit.m中全局变量的值,而其他文件下的全局变量BOOL值依然是默认的NO
。所以导致上述现象中只有XXKit文件下的Log可控,其他文件Log始终不输出。
正确代码思路:
在.m中定义全局变量并使用extern提供给外界,在.m中控制全局变量的值,这样保证全局只有这一个变量而保证外界调用API更改后,Log方法能够正确return。 Talk is cheap,show me the code!
XXLog.h
extern BOOL kLoggingEnabled;
#define XXDebug(fmt,...) XXLog(fmt, ## __VA_ARGS__);
static inline void XXLog(NSString *format, ...) {
//NSLog(@"%p = %@",&kLoggingEnabled,[NSString stringWithFormat:@"%d",kLoggingEnabled]);
if (!kLoggingEnabled) return;
va_list arg_list;
va_start (arg_list, format);
NSString *formattedString = [[NSString alloc] initWithFormat:format arguments:arg_list];
NSLog(@"[XXLog]: %@", formattedString);
va_end(arg_list);
}
XXKit.h
//向外提供了控制内部打印的接口
+ (void)setEnableLog:(BOOL)enableLog;
+ (BOOL)getEnableLog;
XXKit.m
#import “XXLog.h”
//定义全局变量
BOOL kLoggingEnabled = NO;
+ (void)setEnableLog:(BOOL)enableLog {
kLoggingEnabled = enableLog;
}
+ (BOOL)getEnableLog {
return kLoggingEnabled;
}
- (void)testA {
....
XXDebug(@"Log info!!!");
....
}
OtherFile.m
#import “XXLog.h”
- (void)otherFunc {
....
XXDebug(@"OtherFile Log info!!!");
....
}
或者写成对的XXLog.h 和 XXLog.m , 将定义全局变量的活自己全干了(可移植使用)
XXLog.h
#define XXDebug(fmt,...) XXLog(fmt, ## __VA_ARGS__);
void XXLog(NSString *format, ...);
void setLogEnable(BOOL enable);
BOOL logEnable(void);
XXLog.m
static BOOL kLoggingEnabled = NO;
void XXLog(NSString *format, ...) {
//NSLog(@"%p = %@",&kLoggingEnabled,[NSString stringWithFormat:@"%d",kLoggingEnabled]);
if (!kLoggingEnabled) return;
va_list arg_list;
va_start (arg_list, format);
NSString *formattedString = [[NSString alloc] initWithFormat:format arguments:arg_list];
NSLog(@"[XXLog]: %@", formattedString);
va_end(arg_list);
}
void setLogEnable(BOOL enable) {
kLoggingEnabled = enable;
}
BOOL logEnable(void) {
return kLoggingEnabled;
}
优化加锁:
加锁问题:可将全部操作放到主队列简单省事 或者 加锁
- 队列执行
#define XXDebug(fmt,...)\
dispatch_async(dispatch_get_main_queue(), ^{ XXLog(fmt, ## __VA_ARGS__); })
2.加锁执行
XXLog.m
static BOOL kLoggingEnabled = NO;
static NSString *kLockObj = @"kLockConstantObj";
void XXLog(NSString *format, ...) {
@synchronized(kLockObj) {
//NSLog(@"%p = %@",&kLoggingEnabled,[NSString stringWithFormat:@"%d",kLoggingEnabled]);
if (!kLoggingEnabled) return;
va_list arg_list;
va_start (arg_list, format);
NSString *formattedString = [[NSString alloc] initWithFormat:format arguments:arg_list];
NSLog(@"[XXLog]: %@", formattedString);
va_end(arg_list);
}
}
void setLogEnable(BOOL enable) {
@synchronized(kLockObj) {
kLoggingEnabled = enable;
}
}
BOOL logEnable(void) {
@synchronized(kLockObj) {
return kLoggingEnabled;
}
}