Console log。
开发中,我们会经常碰到需要打印一个值,或者在我们的网络层中使用 Log 组件来打印我们的 RequestBody 或者 ResponseBody。但是对于一些容器类中的中文 NSLog 会以人类看不懂的形式 --- Unicode 来展示,但是在 Xcode 的控制台使用 LLVM 的 po 命令就会显示正确的中文,这是为什么呢?且听我为你娓娓道来~
在很多面向对象语言中,对象通常都会有一个 toString() 或者近似的方法。在 Objective-C 中也不例外,不过呢在 Objective-C 中这个方法叫做:
- (NSString *)description;
也有个姊妹函数:
- (NSString *)debugDescription;
这两个方法对于继承自 NSObject 的类都可以使用,通常情况下这两个方法没有区别,description 方法会在我们调用 NSLog 时会调用,debugDescription 只有在我们进行断点调试的时候才会调用。
在 objc.io 的 Issue 9 Working with Strings 中提到 %@ 优先会调用 -descriptionWithLocale:level: ,查阅官方文档后发现 NSArray NSSet NSDictionary 中都有这个方法。
那么,接下我们就该考虑怎么做的问题了。我们需要分别继承 NSArray、 NSSet 、NSDictionary 来重写改造这个方法吗?No No No 万万使不得,这在 Objc 语言中可不是最佳实践,究其原因,我会在后续写一篇文章来阐述为什么不应该,以及如果需要我们应该怎么做的文章,嘻嘻,留点悬念吧~
那么我们应该怎么办呢?究竟怎样才是最佳实践呢?Category wtf?! 不是说不能在 Category 重写主类方法吗? 当然不是咯,我们要知其然更要知其所以然,Category 中重写主类方法其实不是对主类方法进行了覆盖,而是“插队”,runtime 在运作的时候会优先调用排在方法列表前边的方法,而 Category 中的方法列表会排在前边。
接下来我们可以新建一个 FoundationContainer + Log.m 文件,重写 descriptionWithLocale:level: 方法,遍历我们的容器类就好了,示例代码我写在下边了,加了一些制表符使得输出更美观, Locale 和 level 参数含义我不过多介绍了,可以参考官方文档,No Code No BB,下面👇开始上代码:
⚠️:由于源码内容过长,我们也准备了相应的 Demo,需要的朋友们可以关注微信公众号
“iOS 开发技术栈”回复关键字Console Log获取噢~
扫码关注公众号后,回复关键字 Console Log 获取 Demo
#ifdef DEBUG
@implementation NSSet(Log)
- (NSString *)descriptionWithLocale:(id)locale indent:(NSUInteger)level {
NSMutableString *desc = [NSMutableString string];
NSMutableString *originalStr = [[NSMutableString alloc] initWithCapacity:level];
for (NSUInteger i = 0; i < level; ++i) {
[originalStr appendString:@"\t"];
}
NSString *tab = @"\t";
if (level > 0) {
tab = originalStr;
}
[desc appendString:@"\t{(\n"];
[self enumerateObjectsUsingBlock:^(id _Nonnull obj, BOOL * _Nonnull stop) {
if ([obj isKindOfClass:[NSDictionary class]]
|| [obj isKindOfClass:[NSArray class]]
|| [obj isKindOfClass:[NSSet class]]) {
NSString *str = [((NSDictionary *)obj) descriptionWithLocale:locale indent:level + 1];
[desc appendFormat:@"%@\t%@,\n", tab, str];
} else if ([obj isKindOfClass:[NSString class]]) {
[desc appendFormat:@"%@\t\"%@\",\n", tab, obj];
} else if ([obj isKindOfClass:[NSData class]]) {
// if is NSData,try parse
NSError *error = nil;
NSObject *result = [NSJSONSerialization JSONObjectWithData:obj
options:NSJSONReadingMutableContainers
error:&error];
if (error == nil && result != nil) {
if ([result isKindOfClass:[NSDictionary class]]
|| [result isKindOfClass:[NSArray class]]
|| [result isKindOfClass:[NSSet class]]) {
NSString *str = [((NSDictionary *)result) descriptionWithLocale:locale indent:level + 1];
[desc appendFormat:@"%@\t%@,\n", tab, str];
} else if ([obj isKindOfClass:[NSString class]]) {
[desc appendFormat:@"%@\t\"%@\",\n", tab, result];
}
} else {
@try {
NSString *str = [[NSString alloc] initWithData:obj encoding:NSUTF8StringEncoding];
if (str != nil) {
[desc appendFormat:@"%@\t\"%@\",\n", tab, str];
} else {
[desc appendFormat:@"%@\t%@,\n", tab, obj];
}
}
@catch (NSException *exception) {
[desc appendFormat:@"%@\t%@,\n", tab, obj];
}
}
} else {
[desc appendFormat:@"%@\t%@,\n", tab, obj];
}
}];
[desc appendFormat:@"%@)}", tab];
return desc;
}
@end
@implementation NSArray (Log)
- (NSString *)descriptionWithLocale:(id)locale indent:(NSUInteger)level {
NSMutableString *desc = [NSMutableString string];
NSMutableString *originalStr = [[NSMutableString alloc] initWithCapacity:level];
for (NSUInteger i = 0; i < level; ++i) {
[originalStr appendString:@"\t"];
}
NSString *tab = @"";
if (level > 0) {
tab = originalStr;
}
[desc appendString:@"\t(\n"];
[self enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
if ([obj isKindOfClass:[NSDictionary class]]
|| [obj isKindOfClass:[NSArray class]]
|| [obj isKindOfClass:[NSSet class]]) {
NSString *str = [((NSDictionary *)obj) descriptionWithLocale:locale indent:level + 1];
[desc appendFormat:@"%@\t%@,\n", tab, str];
} else if ([obj isKindOfClass:[NSString class]]) {
[desc appendFormat:@"%@\t\"%@\",\n", tab, obj];
} else if ([obj isKindOfClass:[NSData class]]) {
NSError *error = nil;
NSObject *result = [NSJSONSerialization JSONObjectWithData:obj
options:NSJSONReadingMutableContainers
error:&error];
if (error == nil && result != nil) {
if ([result isKindOfClass:[NSDictionary class]]
|| [result isKindOfClass:[NSArray class]]
|| [result isKindOfClass:[NSSet class]]) {
NSString *str = [((NSDictionary *)result) descriptionWithLocale:locale indent:level + 1];
[desc appendFormat:@"%@\t%@,\n", tab, str];
} else if ([obj isKindOfClass:[NSString class]]) {
[desc appendFormat:@"%@\t\"%@\",\n", tab, result];
}
} else {
@try {
NSString *str = [[NSString alloc] initWithData:obj encoding:NSUTF8StringEncoding];
if (str != nil) {
[desc appendFormat:@"%@\t\"%@\",\n", tab, str];
} else {
[desc appendFormat:@"%@\t%@,\n", tab, obj];
}
}
@catch (NSException *exception) {
[desc appendFormat:@"%@\t%@,\n", tab, obj];
}
}
} else {
[desc appendFormat:@"%@\t%@,\n", tab, obj];
}
}];
[desc appendFormat:@"%@)", tab];
return desc;
}
@end
@implementation NSDictionary (Log)
- (NSString *)descriptionWithLocale:(id)locale indent:(NSUInteger)level {
NSMutableString *desc = [NSMutableString string];
NSMutableString *originalStr = [[NSMutableString alloc] initWithCapacity:level];
for (NSUInteger i = 0; i < level; ++i) {
[originalStr appendString:@"\t"];
}
NSString *tab = @"";
if (level > 0) {
tab = originalStr;
}
[desc appendString:@"\t{\n"];
[self.allKeys enumerateObjectsUsingBlock:^(id _Nonnull key, NSUInteger idx, BOOL * _Nonnull stop) {
id obj = [self objectForKey:key];
if ([obj isKindOfClass:[NSString class]]) {
[desc appendFormat:@"%@\t%@ = \"%@\",\n", tab, key, obj];
} else if ([obj isKindOfClass:[NSArray class]]
|| [obj isKindOfClass:[NSDictionary class]]
|| [obj isKindOfClass:[NSSet class]]) {
[desc appendFormat:@"%@\t%@ = %@,\n", tab, key, [obj descriptionWithLocale:locale indent:level + 1]];
} else if ([obj isKindOfClass:[NSData class]]) {
NSError *error = nil;
NSObject *result = [NSJSONSerialization JSONObjectWithData:obj
options:NSJSONReadingMutableContainers
error:&error];
if (error == nil && result != nil) {
if ([result isKindOfClass:[NSDictionary class]]
|| [result isKindOfClass:[NSArray class]]
|| [result isKindOfClass:[NSSet class]]) {
NSString *str = [((NSDictionary *)result) descriptionWithLocale:locale indent:level + 1];
[desc appendFormat:@"%@\t%@ = %@,\n", tab, key, str];
} else if ([obj isKindOfClass:[NSString class]]) {
[desc appendFormat:@"%@\t%@ = \"%@\",\n", tab, key, result];
}
} else {
@try {
NSString *str = [[NSString alloc] initWithData:obj encoding:NSUTF8StringEncoding];
if (str != nil) {
[desc appendFormat:@"%@\t%@ = \"%@\",\n", tab, key, str];
} else {
[desc appendFormat:@"%@\t%@ = %@,\n", tab, key, obj];
}
}
@catch (NSException *exception) {
[desc appendFormat:@"%@\t%@ = %@,\n", tab, key, obj];
}
}
} else {
[desc appendFormat:@"%@\t%@ = %@,\n", tab, key, obj];
}
}];
[desc appendFormat:@"%@}", tab];
return desc;
}
@end
#endif
经过测试一切完美 ؏؏☝ᖗ乛◡乛ᖘ☝؏؏ ,感谢大家收看,记得点击关注哦~
[ 热文推荐 ]-
不知己高的 UITableHeaderView
-
忘不了的 TODOS & FIXMES & ERRORS
-
iOS指定初始化方法的正确使用姿势
-
WWDC 2018 更新了什么?
关注 "iOS开发技术栈" ,看奇技淫巧