1. 程式人生 > >Xcode 控制臺打印Unicode字符串轉換為中文

Xcode 控制臺打印Unicode字符串轉換為中文

間接 定義 nco 原理 字符串轉換 方便 lin cfm oca

在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:0 range:NSMakeRange(0, 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

Xcode 控制臺打印Unicode字符串轉換為中文