Console log
。 开发中,我们会经常碰到需要打印一个值,或者在我们的网络层中使用 Log
组件来打印我们的 RequestBody
或者 ResponseBody
。但是对于一些容器类中的中文 NSLog
会以人类看不懂的形式 --- Unicode
来展示,但是在 Xcode 的控制台使用 LLVM 的 po
命令就会显示正确的中文,这是为什么呢?且听我为你娓娓道来~
在很多面向对象语言中,对象通常都会有一个 toString()
或者近似的方法。在 Objective-C
中也不例外,不过呢在 Objective-C
中这个方法叫做:
- (NSString *)description;复制代码
也有个姊妹函数:
- (NSString *)debugDescription;复制代码
这两个方法对于继承自 NSObject
的类都可以使用,通常情况下这两个方法没有区别,description
方法会在我们调用 NSLog
时会调用,debugDescription
只有在我们进行断点调试的时候才会调用。
在 的 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开发技术栈" ,看奇技淫巧