【iOS】让NSLog打印字典显示得更好看(解决中文乱码并显示成JSON格式)

2024-02-24 16:10

本文主要是介绍【iOS】让NSLog打印字典显示得更好看(解决中文乱码并显示成JSON格式),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前言

文章的初衷很简单,是为了能够正常显示打印出字典里面的中文。因为默认情况下,直接打印字典的话,在Xcode控制台上,中文会是乱码的,需要Unicode转码才能看到中文。
比如打印下面的一个字典

NSDictionary *dict = @{@"ArticleTitle":@"【iOS开发】打开另一个APP(URL Scheme与openURL)",@"ArticleUrl":@"https://www.jianshu.com/p/0811ccd6a65d",@"author":@{@"nickName":@"谦言忘语",@"blog":@"https://www.jianshu.com/u/cc2cf725ac0c",@"work":@"iOS工程师"}};
NSLog(@"打印出的字典:%@",dict);

Xcode控制台上显示的是这样子的:

 

默认情况下Xcode打印字典,中文会显示乱码

 

WTF!谁能告诉我,这坨东西是什么玩意儿?!!!

其实还是可以知道这些Unicode编码是什么意思的。平常我遇到这种情况会复制那堆Unicode的代码到在线网站上进行转码查看。但是依然觉得不太方便。

使用在线网站进行Unicode转码

 

先看看结果

我终于无法忍受这么坑爹的中文显示了,查找一些资料、经过一系列尝试之后,终于找到一个比较满意的解决方案了。先看结果:

 

最终结果

2018-09-03 15:43:10.046 PrintBeautifulLog[4446:1265987] 打印出的字典:{"ArticleTitle" : "【iOS开发】打开另一个APP(URL Scheme与openURL)","ArticleUrl" : "https:\/\/www.jianshu.com\/p\/0811ccd6a65d","author" : {"work" : "iOS工程师","blog" : "https:\/\/www.jianshu.com\/u\/cc2cf725ac0c","nickName" : "谦言忘语"}
}

是不是顿时觉得神清气爽?中文出来了,而且格式也很好看,层次分明。
对了,是不是觉得这个格式似曾相似?
嘿嘿,没错,这个就是JSON格式。不信?我们拿去JSON在线格式化网站上验证下?

JSON格式验证


另外,使用po命令调试打印的时候也是一样的。

po命令调试时也能打印打印出JSON格式的Log

 

直接将文件拖入到工程中即可使用

这么神奇的效果?怎么做到的?嗯,很简单,直接将github仓库上的这两个分类拉入到工程中就可以了。什么代码都不用写。

直接将这两个分类拉入到工程中即可使用

 

怎么做到的?

其实代码很简单,简单到难以想象。分类里面就只有10多行代码。

//NSDictionry分类实现文件代码
#import "NSDictionary+Log.h"
@implementation NSDictionary (Log)
#ifdef DEBUG
//打印到控制台时会调用该方法
- (NSString *)descriptionWithLocale:(id)locale{return self.debugDescription;
}
//有些时候不走上面的方法,而是走这个方法
- (NSString *)descriptionWithLocale:(id)locale indent:(NSUInteger)level{return self.debugDescription;
}
//用po打印调试信息时会调用该方法
- (NSString *)debugDescription{NSError *error = nil;//字典转成jsonNSData *jsonData = [NSJSONSerialization dataWithJSONObject:self options:NSJSONWritingPrettyPrinted  error:&error];//如果报错了就按原先的格式输出if (error) {return [super debugDescription];}NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];return jsonString;
}
#endif
@end

接下来解释下这段代码:

  • NSLog打印字典(NSDictionary)和数组(NSArray)的时候的时候会走- (NSString *)descriptionWithLocale:(id)locale来决定打印的字符串。打印其他对象(比如NSString类型)的时候会走- (NSString *)description方法。所以现在我们需要重写NSDictionary的- (NSString *)descriptionWithLocale:(id)locale方法来得到我们想要的结果。
  • 在使用po命令调试的时候,会走- (NSString *)debugDescription方法,所以我们需要覆盖该方法来显示出我们想要的结果。
  • - (NSString *)descriptionWithLocale:(id)locale- (NSString *)debugDescription方法里面将字典转化为JSON字符串输入,就能同时在代码调试打印和使用po命令调试打印时都能得到我们想要的结果。
    NSError *error = nil;//字典转成json格式字符串NSData *jsonData = [NSJSONSerialization dataWithJSONObject:self options:NSJSONWritingPrettyPrinted  error:&error];NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];return jsonString;
  • 字典转化成字符串有可能会失败,所以失败的时候我们就以默认的格式输出。
if (error) {return [super debugDescription];
}
  • 在分类里面做了DEBUG预编译判断,只有在DEBUG模式下才会调用该方法,线上包(线上包采用Release模式)不会受到影响。
#ifdef DEBUG
//分类中的代码
#endif

嗯,NSArray分类里面的代码也是一毛一样的。所以打印NSArray也能像NSDictionary一样使用JSON格式输出,并且可以正常显示中文。不多说了。

除了 - (NSString *)descriptionWithLocale:(id)locale方法之外,还有一个- (NSString *)descriptionWithLocale:(id)locale indent:(NSUInteger)level方法。这两个方法功能是一样的,后者多了一个indent(缩进)参数。我测试过这两个方法的优先级,发现前后测试的结果有点矛盾,所以就懒得理,两个都实现了。

再看下其他解决NSLog打印字典时中文显示乱码的方式

还有其他的方式也能解决NSLog打印字典时显示乱码的问题。方法是一样的,增加字典和数组的分类,重写- (NSString *)descriptionWithLocale:(id)locale- (NSString *)debugDescription方法,修改Xcode输出字符串。不同之处在于输出字符串的处理方式。先看看常用的方式。

//NSDictionary分类实现文件代码
- (NSString *)descriptionWithLocale:(id)locale{return self.debugDescription;
}
- (NSString *)debugDescription {NSMutableString *strM = [NSMutableString stringWithString:@"{\n"];[self enumerateKeysAndObjectsUsingBlock:^(id key,id obj,BOOL *stop) {[strM appendFormat:@"\t%@ = %@;\n", key, obj];}];[strM appendString:@"}\n"];return strM;
}
//NSArray分类实现文件代码
- (NSString *)descriptionWithLocale:(id)locale{return self.debugDescription;
}
- (NSString *)debugDescription
{NSMutableString *strM = [NSMutableString stringWithString:@"(\n"];[self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx,BOOL *stop) {[strM appendFormat:@"\t%@,\n", obj];}];[strM appendString:@")"];return strM;
}

这种方式是直接遍历字典中的key和value,中间加一个=拼接起来。然后所有的key/value对拼接成一个字符串。每个key/value对后面都加入一个换行符\n。最后在前后加上大括号{}括起来。这种方式可以解决中文显示乱码的问题,但是有一个比较不好的地方,就是缩进格式没有了(Xcode默认的格式是有缩进格式的)。不管里面有多少层嵌套,前面都是一样的间隔。在多层嵌套的时候看起来会不太爽。

遍历key/value对,重新拼接输出字符串

 

上面的方式无法处理缩进格式问题,我们之前提过,使用- (NSString *)descriptionWithLocale:(id)locale indent:(NSUInteger)level方法是有缩进参数的,所以可以使用这个方法可以将缩进格式搞出来。看了下感觉还不错。但是有个小缺点,使用po参数调试的时候就没有办法了。两个方法分写是在NSArray分类和NSDictionary分类里面实现的。代码如下:

//NSArray
- (NSString *)descriptionWithLocale:(nullable id)locale indent:(NSUInteger)level{NSMutableString *mStr = [NSMutableString string];NSMutableString *tab = [NSMutableString stringWithString:@""];for (int i = 0; i < level; i++) {[tab appendString:@"\t"];}[mStr appendString:@"(\n"];for (int i = 0; i < self.count; i++) {NSString *lastSymbol = (self.count == i + 1) ? @"":@",";id value = self[i];if ([value respondsToSelector:@selector(descriptionWithLocale:indent:)]) {[mStr appendFormat:@"\t%@%@%@\n",tab,[value descriptionWithLocale:locale indent:level + 1],lastSymbol];} else {[mStr appendFormat:@"\t%@%@%@\n",tab,value,lastSymbol];}}[mStr appendFormat:@"%@)",tab];return mStr;
}
//NSDictionary
- (NSString *)descriptionWithLocale:(id)locale indent:(NSUInteger)level
{NSMutableString *mStr = [NSMutableString string];NSMutableString *tab = [NSMutableString stringWithString:@""];for (int i = 0; i < level; i++) {[tab appendString:@"\t"];}[mStr appendString:@"{\n"];NSArray *allKey = self.allKeys;for (int i = 0; i < allKey.count; i++) {id value = self[allKey[i]];NSString *lastSymbol = (allKey.count == i + 1) ? @"":@";";if ([value respondsToSelector:@selector(descriptionWithLocale:indent:)]) {[mStr appendFormat:@"\t%@%@ = %@%@\n",tab,allKey[i],[value descriptionWithLocale:locale indent:level + 1],lastSymbol];} else {
[mStr appendFormat:@"\t%@%@ = %@%@\n",tab,allKey[i],value,lastSymbol];}}[mStr appendFormat:@"%@}",tab];return mStr;
}

还有另外一种方式,这种方式的思想是,上面第一种方式没有缩进格式,看起来很不爽,但是系统默认的实现方式是有缩进格式的。只是中文显示有问题而已。那我直接把默认方式中要输出的字符串进行Unicode转化,将其转化为中文不就可以了?
具体代码就不贴了,有兴趣可以看下这篇文章
这种方式确实可行,跟原先的输出的唯一不同就是将Unicode字符串转化为了中文字符串显示。但是有一个缺点,那就是在将默认方式的Unicode字符串转化为中文字符串显示的时候,容易出问题。因为转码之前是需要暴力替换的,这个替换过程是很容易出问题的。比如如果字典的value字符串里面本来就有" "符号,那转码就出问题了。

更新(20180914)

之前的方式遇到字典数组里面有模型的情况容易出问题。
于是在将字典/数组转换成JSON字符串之前,先判断其是否能转换成JSON格式字符串,如果不能,就调用系统的原始实现。
由于要调用系统的原始实现,所以还使用了method swizzle交换了上面说的3个系统方法。具体可查看github代码。

参考

代码已放在github上
iOS JSON数据NSLog小技巧
iOS 打印中文字典,数组,控制台输出中文,并保持缩进格式
iOS description方法和descriptionWithLocale:方法 解决中文现问题
xcode8控制台打印出字典和数组中的中文字符 解决中文乱码
iOS开发实战tips--让Xcode的控制台支持NSArray和NSDictionary的中文输出
从NSDictionary打印不出中文开始

 



作者:谦言忘语
链接:https://www.jianshu.com/p/79cd2476287d
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

这篇关于【iOS】让NSLog打印字典显示得更好看(解决中文乱码并显示成JSON格式)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/742638

相关文章

Java循环创建对象内存溢出的解决方法

《Java循环创建对象内存溢出的解决方法》在Java中,如果在循环中不当地创建大量对象而不及时释放内存,很容易导致内存溢出(OutOfMemoryError),所以本文给大家介绍了Java循环创建对象... 目录问题1. 解决方案2. 示例代码2.1 原始版本(可能导致内存溢出)2.2 修改后的版本问题在

大数据小内存排序问题如何巧妙解决

《大数据小内存排序问题如何巧妙解决》文章介绍了大数据小内存排序的三种方法:数据库排序、分治法和位图法,数据库排序简单但速度慢,对设备要求高;分治法高效但实现复杂;位图法可读性差,但存储空间受限... 目录三种方法:方法概要数据库排序(http://www.chinasem.cn对数据库设备要求较高)分治法(常

Vue项目中Element UI组件未注册的问题原因及解决方法

《Vue项目中ElementUI组件未注册的问题原因及解决方法》在Vue项目中使用ElementUI组件库时,开发者可能会遇到一些常见问题,例如组件未正确注册导致的警告或错误,本文将详细探讨这些问题... 目录引言一、问题背景1.1 错误信息分析1.2 问题原因二、解决方法2.1 全局引入 Element

如何设置vim永久显示行号

《如何设置vim永久显示行号》在Linux环境下,vim默认不显示行号,这在程序编译出错时定位错误语句非常不便,通过修改vim配置文件vimrc,可以在每次打开vim时永久显示行号... 目录设置vim永久显示行号1.临时显示行号2.永www.chinasem.cn久显示行号总结设置vim永久显示行号在li

linux报错INFO:task xxxxxx:634 blocked for more than 120 seconds.三种解决方式

《linux报错INFO:taskxxxxxx:634blockedformorethan120seconds.三种解决方式》文章描述了一个Linux最小系统运行时出现的“hung_ta... 目录1.问题描述2.解决办法2.1 缩小文件系统缓存大小2.2 修改系统IO调度策略2.3 取消120秒时间限制3

C#使用HttpClient进行Post请求出现超时问题的解决及优化

《C#使用HttpClient进行Post请求出现超时问题的解决及优化》最近我的控制台程序发现有时候总是出现请求超时等问题,通常好几分钟最多只有3-4个请求,在使用apipost发现并发10个5分钟也... 目录优化结论单例HttpClient连接池耗尽和并发并发异步最终优化后优化结论我直接上优化结论吧,

IDEA如何将String类型转json格式

《IDEA如何将String类型转json格式》在Java中,字符串字面量中的转义字符会被自动转换,但通过网络获取的字符串可能不会自动转换,为了解决IDEA无法识别JSON字符串的问题,可以在本地对字... 目录问题描述问题原因解决方案总结问题描述最近做项目需要使用Ai生成json,可生成String类型

Go语言实现将中文转化为拼音功能

《Go语言实现将中文转化为拼音功能》这篇文章主要为大家详细介绍了Go语言中如何实现将中文转化为拼音功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 有这么一个需求:新用户入职 创建一系列账号比较麻烦,打算通过接口传入姓名进行初始化。想把姓名转化成拼音。因为有些账号即需要中文也需要英

解决systemctl reload nginx重启Nginx服务报错:Job for nginx.service invalid问题

《解决systemctlreloadnginx重启Nginx服务报错:Jobfornginx.serviceinvalid问题》文章描述了通过`systemctlstatusnginx.se... 目录systemctl reload nginx重启Nginx服务报错:Job for nginx.javas

Mysql DATETIME 毫秒坑的解决

《MysqlDATETIME毫秒坑的解决》本文主要介绍了MysqlDATETIME毫秒坑的解决,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着... 今天写代码突发一个诡异的 bug,代码逻辑大概如下。1. 新增退款单记录boolean save = s