Xcode4中Objective-C调试技巧?

16

从Flex-Flash IDE转过来后,我可以在代码中设置断点,并在运行时查看相应窗口中变量的值。

现在,我已经找到了Xcode中“变量”窗口,可以看到所有的变量,但是这些变量的值是无法被看到的。我只能看到数据类型和一堆十六进制数(指针?)。

我该如何调试我的代码?在不必记录日志的情况下,我在哪里可以看到我的变量值?

我正在尝试查看一个带有大量键/值对的NSDictionary的值。但同样适用于任何其他NSObject。我读到过覆盖描述方法的内容,但原生对象怎么处理?


1
右键点击变量并选择“打印描述”。或者在绿色的“(gdb)”提示符下聚焦控制台,输入:po objectNamepo [object message]。对于字典类型,请输入 po [dic objectForKey:@"somekey"] - Jano
2个回答

39

使用GDB进行调试

XCode提供了GDB调试器,就像Jano在他的评论中所说,你可以使用GDB命令如po(输出对象)来查看一个对象。

po myObject
po myDictionary
po myArray

要打印基本类型如int、float,可以使用printppx(以十六进制查看数字)

print myInt
p myInt
px myInt

您也可以查看运行命令的结果。例如,要查看字符串长度,可以执行以下操作:

p (int) [myString length]

如果您不将返回值转换为int类型,我相信您会在控制台上看到一些投诉。
要查看UIView的框架(CGRect结构类型),可以执行以下操作:
p (CGRect) [myView frame]

最后,如果你重写类的description方法,则可以自定义其在控制台或NSLog中写入时的显示方式。如果执行[NSString stringWithFormat:@"My object... %@", myObj],那么对象的description方法将被调用。
- (NSString*) description
{
   return @"This is the object description!";
}

另一篇不错的文章是如何基于对象字符串属性在Xcode中设置条件断点?


日志提示

如果您想在调试版本中获取NSLog消息,您可能会喜欢我们在工作中使用的DLog宏:

#ifdef DEBUG
    #define DLog(...) NSLog(__VA_ARGS__)
#else
    #define DLog(...) /* */
#endif

它的作用类似于NSLog,但在非DEBUG构建中被编译。 NSLog实际上可能会影响性能,而且您可能不希望一些消息泄漏到日志中。

我们将此宏放在预编译头文件(MyApp-Prefix.pch)中,以便它包含在所有项目文件中。


变量转储

您的评论询问如何在不编写代码的情况下转储对象的所有变量。我不知道有没有内置的方法可以实现这一点。但是,您可以尝试使用反射。我有一种实现方法,可以让您执行类似于以下操作:

po [someObj dump]

你可以在NSObject上创建一个分类,以添加一个方法到所有的NSObject类型中,该方法将输出你想要的信息。我从Objective C Introspection/Reflection中借鉴了代码来启动这个过程,但是添加了包括属性值的代码。
NSObject(DebuggingAid)分类:
#import <objc/runtime.h>
@interface NSObject (DebuggingAid)

- (NSString*)dump;

@end

@implementation NSObject (DebuggingAid)

- (NSString*)dump
{
    if ([self isKindOfClass:[NSNumber class]] ||
        [self isKindOfClass:[NSString class]] ||
        [self isKindOfClass:[NSValue class]])
    {
        return [NSString stringWithFormat:@"%@", self];
    }

    Class class = [self class];
    u_int count;

    Ivar* ivars = class_copyIvarList(class, &count);
    NSMutableDictionary* ivarDictionary = [NSMutableDictionary dictionaryWithCapacity:count];
    for (int i = 0; i < count ; i++)
    {
        const char* ivarName = ivar_getName(ivars[i]);
        NSString *ivarStr = [NSString stringWithCString:ivarName encoding:NSUTF8StringEncoding];
        id obj = [self valueForKey:ivarStr];
        if (obj == nil)
        {
            obj = [NSNull null];
        }
        [ivarDictionary setObject:obj forKey:ivarStr];
    }
    free(ivars);

    objc_property_t* properties = class_copyPropertyList(class, &count);
    NSMutableDictionary* propertyDictionary = [NSMutableDictionary dictionaryWithCapacity:count];
    for (int i = 0; i < count ; i++)
    {
        const char* propertyName = property_getName(properties[i]);
        NSString *propertyStr = [NSString  stringWithCString:propertyName encoding:NSUTF8StringEncoding];
        id obj = [self valueForKey:propertyStr];
        if (obj == nil)
        {
            obj = [NSNull null];
        }
        [propertyDictionary setObject:obj forKey:propertyStr];
    }
    free(properties);

    NSDictionary* classDump = [NSDictionary dictionaryWithObjectsAndKeys:
                               ivarDictionary, @"ivars",
                               propertyDictionary, @"properties",
                               nil];
    NSString *dumpStr = [NSString stringWithFormat:@"%@", classDump];

    return dumpStr;
}

@end

忘了提一下,但如果你执行 po myDictionary,你应该能看到最初想要的键/值对。 - Sam
1
太棒了!有没有一种递归打印对象的方法?例如,如果我有一个Person对象,其中包含一个NSString 'address'属性,现在我调用'po [Person address]。我想一次看到它的所有属性(比如姓名、电话等),而不必编写自己的实现'description'方法来返回它们。 - Jovan
我在答案中为NSObject添加了一个类别,这将帮助您完成部分工作。这不是递归的。您可以使其递归,但需要放置某些内容以不探索循环引用。 - Sam
另外,参见Xcode 4.2调试无法解析堆栈调用,了解如何在只给出一组内存地址(十六进制数)时获取堆栈跟踪。此外,您可以使用info symbol 0x123456,其中0x123456应替换为内存地址。这将在该内存地址上输出符号信息(如函数名称)。 - Sam

5
假设您设置了断点并且程序已在其中停止,您将在调试区域左侧的变量视图中看到一系列变量。每个变量都有一个值。如果它是原始类型,则没有更多内容可见,但如果它是对象,则只会显示地址和展开箭头。展开箭头将显示对象的所有实例变量。通过这种方式,您可以深入挖掘您要查找的内容。
很多时候,您拥有公开属性的对象,但不显式存储该属性或以另一种形式存储该属性。调试器无法跟踪这些,但是您可以在右侧的GDB窗口中评估任何Objective-C表达式,包括执行方法调用。我认为最有用的命令是'p'打印原始对象和'po'打印对象类型的描述。
在Foundation类型中,变量视图窗口似乎足够聪明,能够列出NSArray的内容,但不足以提供有关字典的任何有意义的信息。您只会被告知例如“1个键/值对”,但除此之外没有更多信息。因此,您需要进入右侧的窗口并键入“po dictionary”以查看内容。
我个人认为Xcode中的图形状态检查非常弱;我发现调试器完全有效,并且可以轻松跟踪我的代码并查找错误,仅因为我学会了使用右侧的控制台窗口。

5
在Xcode中,图形状态检查非常薄弱。 - phlebotinum

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接