13

当以编程方式指定条件时,我已经成功地让调试版本停止执行,使用目标 C 中的标准 NSAssert(condition_which_should_evaluate_true, @"error message") 语句,并在断点导航器中添加“所有异常”断点。

很好,但是大多数时候我在调试时,我也想在那之后继续正常的程序执行。通常在断言失败后继续程序有助于追踪混淆/错误的来源。至少据我记得当我在不同的平台上编程时。

在 Objective C 开发中是否有这样做的标准方法?

4

7 回答 7

15

有办法。这不是 Objective-C 的东西,而是 Unix 的东西。

kill(getpid(), SIGSTOP);

或者简单地说:

raise(SIGSTOP);

在斯威夫特:

raise(SIGSTOP)

这将在__killor__pthread_kill函数中的调试器中中断。然后,您将需要向上几个堆栈帧来查看调用killor的帧raise。您可以使用调试器的 continue 命令恢复执行。

请注意,如果您没有在调试器下运行并执行此操作,您的应用程序将挂起。看看 [技术问答 QA1631:检测调试器]( http://developer.apple.com/library/mac/#qa/qa1361/_index.html。您可以使用该信息编写包装函数或宏仅SIGSTOP在调试器下运行时发送。这个答案可能会有所帮助。

此外,Foundation 框架提供了一个不同的断言宏以用于常规函数。是NSCAssert

于 2012-08-07T03:04:19.570 回答
2

听起来您想使用条件断点。如果您通过单击源代码的边缘设置断点,然后按住 ctrl 并单击蓝色的小断点,您可以编辑一些选项,包括使断点以变量的值为条件。

这是一篇包含一些屏幕截图和更多信息的博客文章。

这个 Stack Overflow 问题也有一些很好的提示。


如果您坚持以编程方式触发断点,则编写一个函数并在其中放置一个断点:

void MyConditionalBreak(BOOL condition, NSString *comment)
{
    if (condition) {
        NSLog(@"Stopped because %@", comment); // SET BREAKPOINT ON THIS LINE
    }
}

然后你可以用与 NSAssert 类似的方式调用这个函数。如果您在项目的预编译头文件 (Whatever.pch) 中声明该函数,它将在您的所有源文件中可用,而无需显式#import任何内容。

于 2012-08-07T00:09:09.210 回答
2

这是我的做法:

首先,在断点选项卡中,如果引发任何异常,我将我的应用程序设置为中断: 所有例外

然后在代码中(我通常有通用头文件,其中包含我在各处导入的通用定义):

static void ThrowException(NSString* reason)
{
   @try 
   {
      @throw [NSException
               exceptionWithName:@"DebugAssertionException"
               reason:reason
               userInfo:nil];
   }  
   @catch (NSException * e) 
   {
      NSLog(@"%@", e);
   }
}

#define MYAssert(test, fmt, ...) if (!(test)) { ThrowException([NSString stringWithFormat:@"%s !!! ASSERT !!! " fmt, __PRETTY_FUNCTION__, ##__VA_ARGS__]); }

现在,您可以像使用 NSAssert 一样使用它,但不是杀死您的应用程序,您只需触发一个断点:

MYAssert(bEverythingOkay, @"Something went wrong!");

// Or with arguments of course
MYAssert(bEverythingOkay, @"Something went wrong (TestValue=%zd; Reason=%@)", myTestValue, [this getLastError]);
于 2015-10-18T06:51:18.623 回答
1

我绝不是该领域的专家,但我使用的代码可以通过键盘输入进入调试器。

DCIntrospect来自 github 上的国内猫软件。

看看它的主文件DCIntrospect.m的顶部,看看它是如何做到的。

它引用了一些来源,但根据我的经验,它与破解 armv6/7 上的调试器和模拟器所需的当前程序集相当最新

更多背景信息的外部参考

于 2012-08-07T01:14:09.370 回答
1

不知道为什么没有人给出明确直接的答案......已经五年了,但迟到总比没有好..


预处理器:

#ifdef DEBUG
#define manualBreakpoint() \
            NSLog(@"\n\n\
                    Breakpoint called on: \n\n\
                    File: %s \n\n\
                    Line number: %i", __FILE__, __LINE__);\
                                                          \
            raise(SIGSTOP)
#else
#define manualBreakpoint() ;
#endif

用法:

要使用它,只需键入以下内容:manualBreakpoint();

笔记:

这将在调用该代码时停止应用程序并在堆栈跟踪中显示当前方法(以及记录应用程序已停止的文件名和行号)如果您处于调试模式,则如果您处于生产模式appstore (又名releasedistributionarchive)它什么也不做。

于 2017-04-28T04:50:31.173 回答
0

使用 Rob 的评论,以及来自“ios5 Programming: Pushing the Limits”和 Lumberjack 框架的一些想法,这里有一个宏可以让调试器在构建中的断言期间停止并允许继续DEBUG,但在其他情况下像往常一样在RELEASE(或实际上任何非-DEBUG)构建。

#ifdef DEBUG

#define MyAssert(condition, desc, ...) \
if (!(condition)) { \
    NSLog((desc), ## __VA_ARGS__); \
    if (AmIBeingDebugged()) \
        kill (getpid(), SIGSTOP); \
    else { \
        NSLog(@"%@, %d: could not break into debugger.", THIS_FILE, __LINE__); \
    } \
}

#define MyCAssert(condition, desc, ...) \
if (!(condition)) { \
    NSLog((desc), ## __VA_ARGS__); \
    if (AmIBeingDebugged()) \
        kill (getpid(), SIGSTOP); \
    else \
        NSLog(@"%@, %d: could not break into debugger.", THIS_FILE, __LINE__)); \
    } \
}

#else  //NOT in DEBUG

#define MyAssert(condition, desc, ...) \
if (!(condition)) { \
    DDLogError((desc), ## __VA_ARGS__); \   //use NSLog if not using Lumberjack
    NSAssert((condition), (desc), ## __VA_ARGS__); \
}

#define MyCAssert(condition, desc, ...) \
if (!(condition)) { \
    DDLogError((desc), ## __VA_ARGS__); \   //use NSLog if not using Lumberjack
    NSCAssert((condition), (desc), ## __VA_ARGS__); \
}

#endif //end  DEBUG

#endif

这些宏需要函数AmIBeingDebugged(),您可以通过 Rob 提供的链接从 Apple 获得该函数:技术问答 QA1631:检测调试器。(您还需要DEBUG在构建设置中定义。)

请注意,我在非构建中选择了 Lumberjack's DDLogError()over ,因为它会吐出发生致命断言的方法、文件和行号。NSLog()DEBUG

于 2012-08-24T02:55:24.163 回答
0

如果您想使用控制台,一个简单的技巧是:

if (!condition_which_should_evaluate_true) {
   ; // Put breakpoint here
}

然后将断点放在该行内。如果条件评估为 NO,则调用您的断点。

于 2013-10-24T20:20:45.013 回答