在调试器(或日志)中类似NSDictionary的漂亮打印

时间:2014-01-25 04:44:16

标签: ios objective-c xcode debugging lldb

这一直困扰着我。如何使用po foo(或通过NSLog)在调试器中转储对象时,如何抵消发生的丑陋转义。我尝试了很多方法来实现-description-debugDescription无济于事。

鉴于这个简单的课程

@interface Foo : NSObject
@property NSDictionary* dict;
@end

@implementation Foo
- (NSString *)description {
    // super.description for the <{classname} pointer> output
    return [NSString stringWithFormat:@"%@ %@", super.description, self.dict];
}
@end

设计用法

Foo* f0 = [[Foo alloc] init];
f0.dict = @{ @"value": @0, @"next": NSNull.null };
Foo* f1 = [[Foo alloc] init];
f1.dict = @{ @"value": @1, @"next": f0 };
Foo* f2 = [[Foo alloc] init];
f2.dict = @{ @"value": @2, @"next": f1 };

我们为f0

获得了不错的输出
(lldb) po f0
<Foo: 0x8cbc410> {
    next = "<null>";
    value = 0;
}

f1

的可容忍输出
(lldb) po f1
<Foo: 0x8cbc480> {
    next = "<Foo: 0x8cbc410> {\n    next = \"<null>\";\n    value = 0;\n}";
    value = 1;
}

f2

的可怕输出
(lldb) po f2
<Foo: 0x8cbc4b0> {
    next = "<Foo: 0x8cbc480> {\n    next = \"<Foo: 0x8cbc410> {\\n    next = \\\"<null>\\\";\\n    value = 0;\\n}\";\n    value = 1;\n}";
    value = 2;
}

调试真实世界对象层次结构时,很难快速解析。我假设自从转储类似嵌套的NSDictionary

以来我还缺少其他一些技巧
NSDictionary* d0 = @{ @"value": @0, @"next": NSNull.null };
NSDictionary* d1 = @{ @"value": @1, @"next": d0 };
NSDictionary* d2 = @{ @"value": @2, @"next": d1 };

保持缩进并避免逃避地狱

(lldb) po d2
{
    next =     {
        next =         {
            next = "<null>";
            value = 0;
        };
        value = 1;
    };
    value = 2;
}

更新

切换到-debugDescription并简单地转发到词典

@implementation Foo
- (NSString *)debugDescription {
    return self.dict.debugDescription;
}
@end

丢失递归输出

(lldb) po f2
{
    next = "<Foo: 0x8b70e20>";
    value = 2;
}

内部NSDictionary必须依赖于我在此示例中未实现的-description,仅-debugDescription。切换到类似下面的内容

@implementation Foo
- (NSString *)description {
    return self.dict.description;
}
- (NSString *)debugDescription {
    return self.dict.debugDescription;
}
@end

也会产生类似的错误输出

(lldb) po f2
{
    next = "{\n    next = \"{\\n    next = \\\"<null>\\\";\\n    value = 0;\\n}\";\n    value = 1;\n}";
    value = 2;
}

2 个答案:

答案 0 :(得分:4)

TL; DR;

使用NSContainers-PrettyPrint并谨慎read docs

长答案

经过更多搜索后,我发现了descriptionWithLocale:indent:方法。如上所述,我应该能够在我自己的类中实现它,以实现所需的漂亮打印格式。但是,经过一些尝试失败后我找到了similar SO question。事实证明descriptionWithLocale:indent:仅在您因为“安全问题”而继承基础容器类时才有效。

对这种方法不满意我继续挖掘并找到了这个radar,但也是NSContainers-PrettyPrint中的解决方案。经过一些反复试验后,我得到了很好的工作。 (它不在CocoaPods上,所以你必须手动添加它。)

添加NSContainers-PrettyPrint后,您可能也需要JRSwizzle。然后在DEBUG目标预处理器宏中定义DEBUGPRINT_ALLDEBUGPRINT_SWIZZLE。最后,您可以根据fs_* helpersbest practices实施descriptionWithLocale:indent:方法。

使用我问题中的Foo作为示例

@implementation Foo
- (NSString*)description
{
    return [NSString stringWithFormat:@"%@ %@", super.description, self.dict.description];
}

- (NSString *)descriptionWithLocale:(id)locale indent:(NSUInteger)level
{
    NSString * indent = [NSString fs_stringByFillingWithCharacter:' ' repeated:fspp_spacesPerIndent*level];
    NSMutableString * str = [[NSMutableString alloc] init];
    [str fs_appendObjectStartWithIndentString:indent caller:self];
    [str appendString:[self.dict descriptionWithLocale:locale indent:level+1]];
    [str fs_appendObjectEnd];
    return str;
}
@end

在相同f0f1f2个实例

的情况下,会产生以下输出
(lldb) po f0
<Foo: 0x8a385c0> {
    value = 0;
    next = <null>;
}
(lldb) po f1
<Foo: 0x8a38630> {
    value = 1;
    next = <Foo:0x8a385c0        {
            value = 0;
            next = <null>;
        }>;
}
(lldb) po f2
<Foo: 0x8a38660> {
    value = 2;
    next = <Foo:0x8a38630        {
            value = 1;
            next = <Foo:0x8a385c0                {
                    value = 0;
                    next = <null>;
                }>;
        }>;
}

以上descriptionWithLocale:indent:可以使用一些调整来减少过多的空白,但它仍然可以替代它。

答案 1 :(得分:2)

类似的问题(NSDictionary description formatting problem — treats structure like char data)表明NSArrayNSDictionary在某种程度上作弊而不是-[NSObject respondsToSelector:@selector(descriptionWithLocale:indent:)。这似乎是真的。

为了测试这个,我创建了一个NSProxy来记录所有的电话。测试是:

NSObject *proxy = [LoggingProxy proxyWithTarget:[[NSObject alloc] init]];
NSLog(@"%@", @[proxy]);

结果是:

message: isNSString__
message: isNSDictionary__
message: isNSArray__
message: isNSData__

2014-05-29 15:29:22.728 Proxy[36219:303] (
    "<LoggingProxy: 0x100103510>"
)
Program ended with exit code: 0

在我的真实物体中,我添加了方法:

#if DEBUG
- (BOOL) isNSDictionary__
{
    return YES;
}
#endif

NSArrayNSDictionary然后通过致电-[NSObject respondsToSelector:@selector(descriptionWithLocale:indent:)表现出您的希望。确保如果您返回YES,那么您确实已经实施了descriptionWithLocale:indent:,因为它会被调用,而不会检查它是否真的存在。

请记住将此方法包装在 #if DEBUG中。你真的不想发货,但在Xcode中使用它似乎很好。

相关问题