修复看不懂的 Console Log

331 阅读5分钟

大家好~ 我是大萌鱼。今天我跟大家分享的题目是:修复看不懂的 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 中都有这个方法。

那么,接下我们就该考虑怎么做的问题了。我们需要分别继承 NSArrayNSSetNSDictionary 来重写改造这个方法吗?No No No 万万使不得,这在 Objc 语言中可不是最佳实践,究其原因,我会在后续写一篇文章来阐述为什么不应该,以及如果需要我们应该怎么做的文章,嘻嘻,留点悬念吧~

那么我们应该怎么办呢?究竟怎样才是最佳实践呢?Category wtf?! 不是说不能在 Category 重写主类方法吗? 当然不是咯,我们要知其然更要知其所以然,Category 中重写主类方法其实不是对主类方法进行了覆盖,而是“插队”,runtime 在运作的时候会优先调用排在方法列表前边的方法,而 Category 中的方法列表会排在前边。

接下来我们可以新建一个 FoundationContainer + Log.m 文件,重写 descriptionWithLocale:level: 方法,遍历我们的容器类就好了,示例代码我写在下边了,加了一些制表符使得输出更美观, Localelevel 参数含义我不过多介绍了,可以参考官方文档,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开发技术栈" ,看奇技淫巧