Xcode 控制台打印Unicode字符串转换为中文

时间:2022-01-03 14:53:23

在Xcode的控制台里直接打印一个数组或者字典,输出的都是一些Unicode的编码,不方便调试.    要想看到中文,则要去获取对应的key或者数组下标.得到具体某一个对象才能看到中文,给我们调试起来很不方便.

而现在可以使用LYLUnicode输出中文, 使用也方便.代码也简洁,就几行代码. 直接把LYLUnicode拖到工程里就能让Xcode支持中文的输出了.

就这么简单的一行输出,不需要任何更改.,使用之前Xcode控制台是打印的Unicode编码, 把LYLUnicode拖进来之后,不做任何操作,输出的内容就是我们想看到的中文了.

LYLUnicode的原理比较简单, 代码也很简洁,几行代码,轻轻松松看懂.

原理就是利用runtime替换原有的description和descriptionWithLocale:

还有descriptionWithLocale:indent:这几个方法.并转成让Xcode支持中文的编码.

编码转换代码:

- (NSString *)stringByReplaceUnicode:(NSString *)unicodeString
{
NSMutableString *convertedString = [unicodeString mutableCopy];
[convertedString replaceOccurrencesOfString:@"\\U" withString:@"\\u" options: range:NSMakeRange(, convertedString.length)];
CFStringRef transform = CFSTR("Any-Hex/Java");
CFStringTransform((__bridge CFMutableStringRef)convertedString, NULL, transform, YES); return convertedString;
}
虽然这样可以随便做category手动调用了,但还是需要引用头文件,不方便。所以需要做Method Swizzling,替换掉系统的description相关方法,在自定义交换方法中添加如上述代码类似的编码转换操作,一劳永逸(。◕ฺ∀◕ฺ)。具体可以是对NSDictionaryNSArray以及NSSet作一个category,在类被初始加载调用load方法时做Method Swizzling:
@implementation NSDictionary (LYLUnicode)

+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class class = [self class];
LYL_swizzleSelector(class, @selector(description), @selector(LYL_description));
LYL_swizzleSelector(class, @selector(descriptionWithLocale:), @selector(LYL_descriptionWithLocale:));
LYL_swizzleSelector(class, @selector(descriptionWithLocale:indent:), @selector(LYL_descriptionWithLocale:indent:));
});
} - (NSString *)LYL_description {
return [[self LYL_description] stringByReplaceUnicode];
} - (NSString *)LYL_descriptionWithLocale:(nullable id)locale {
return [[self LYL_descriptionWithLocale:locale] stringByReplaceUnicode];
} - (NSString *)LYL_descriptionWithLocale:(nullable id)locale indent:(NSUInteger)level {
return [[self LYL_descriptionWithLocale:locale indent:level] stringByReplaceUnicode];
} @end

swizzleSelector中交换了两个方法的实现。所以方法中调用zx_descriptionWithLocale:indent:其实是调用的原生的descriptionWithLocale:indent:方法。在系统方法返回时,进行编码转换就OK啦(。◕ฺ∀◕ฺ)。
原生的descriptionWithLocale:indent:方法是这样获取描述的: 如果元素是NSString对象则直接返回它; 当元素响应descriptionWithLocale:indent:方法时,调用方法获得该元素的字符串描述; 当元素响应descriptionWithLocale:方法时,调用方法获得该元素的字符串描述; 如果上述条件都不符合,就会调用该元素的description方法获取字符串描述。

原生方法执行遍历子元素的时候,还是会调用descriptionWithLocale:indent:方法来获取子元素的描述,当原生方法被调用时,因方法实现的交换又会执行自定义的交换方法的代码,形成间接递归,上述条件符合时原生方法会返回正确描述开始回归,回归时依次进行编码转换。这样有个小小的问题,就是回归过程中已经被编码转换过的字符串有可能会被重复转换好多次。这里我们交换的是descriptionWithLocale:indent:这一个原生方法。如果可以的话,可以只交换原生的description方法,然后输出的时候调一下description(例如你有个字典dict,这样打印:NSLog(@"%@", dic.description)), 这么做只是将系统方法返回的最终描述进行了一次编码转换。ps: 如果你不嫌麻烦的话,可以这么用 ๑乛◡乛๑ 。。。

补上Method Swizzling代码:

static inline void LYL_swizzleSelector(Class class, SEL originalSelector, SEL swizzledSelector) {
Method originalMethod = class_getInstanceMethod(class, originalSelector);
Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
if (class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod))) {
class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
} else {
method_exchangeImplementations(originalMethod, swizzledMethod);
}
}

githud地址:https://github.com/allencelee/LYLUnicode