当以编程方式指定条件时,我已经成功地让调试版本停止执行,使用目标 C 中的标准 NSAssert(condition_which_should_evaluate_true, @"error message") 语句,并在断点导航器中添加“所有异常”断点。
很好,但是大多数时候我在调试时,我也想在那之后继续正常的程序执行。通常在断言失败后继续程序有助于追踪混淆/错误的来源。至少据我记得当我在不同的平台上编程时。
在 Objective C 开发中是否有这样做的标准方法?
当以编程方式指定条件时,我已经成功地让调试版本停止执行,使用目标 C 中的标准 NSAssert(condition_which_should_evaluate_true, @"error message") 语句,并在断点导航器中添加“所有异常”断点。
很好,但是大多数时候我在调试时,我也想在那之后继续正常的程序执行。通常在断言失败后继续程序有助于追踪混淆/错误的来源。至少据我记得当我在不同的平台上编程时。
在 Objective C 开发中是否有这样做的标准方法?
有办法。这不是 Objective-C 的东西,而是 Unix 的东西。
kill(getpid(), SIGSTOP);
或者简单地说:
raise(SIGSTOP);
在斯威夫特:
raise(SIGSTOP)
这将在__kill
or__pthread_kill
函数中的调试器中中断。然后,您将需要向上几个堆栈帧来查看调用kill
or的帧raise
。您可以使用调试器的 continue 命令恢复执行。
请注意,如果您没有在调试器下运行并执行此操作,您的应用程序将挂起。看看 [技术问答 QA1631:检测调试器]( http://developer.apple.com/library/mac/#qa/qa1361/_index.html。您可以使用该信息编写包装函数或宏仅SIGSTOP
在调试器下运行时发送。这个答案可能会有所帮助。
此外,Foundation 框架提供了一个不同的断言宏以用于常规函数。是NSCAssert
。
听起来您想使用条件断点。如果您通过单击源代码的边缘设置断点,然后按住 ctrl 并单击蓝色的小断点,您可以编辑一些选项,包括使断点以变量的值为条件。
这是一篇包含一些屏幕截图和更多信息的博客文章。
这个 Stack Overflow 问题也有一些很好的提示。
如果您坚持以编程方式触发断点,则编写一个函数并在其中放置一个断点:
void MyConditionalBreak(BOOL condition, NSString *comment)
{
if (condition) {
NSLog(@"Stopped because %@", comment); // SET BREAKPOINT ON THIS LINE
}
}
然后你可以用与 NSAssert 类似的方式调用这个函数。如果您在项目的预编译头文件 (Whatever.pch) 中声明该函数,它将在您的所有源文件中可用,而无需显式#import
任何内容。
这是我的做法:
首先,在断点选项卡中,如果引发任何异常,我将我的应用程序设置为中断:
然后在代码中(我通常有通用头文件,其中包含我在各处导入的通用定义):
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]);
我绝不是该领域的专家,但我使用的代码可以通过键盘输入进入调试器。
DCIntrospect来自 github 上的国内猫软件。
看看它的主文件DCIntrospect.m的顶部,看看它是如何做到的。
它引用了一些来源,但根据我的经验,它与破解 armv6/7 上的调试器和模拟器所需的当前程序集相当最新
更多背景信息的外部参考
不知道为什么没有人给出明确直接的答案......已经五年了,但迟到总比没有好..
#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 (又名release、distribution或archive)它什么也不做。
使用 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
如果您想使用控制台,一个简单的技巧是:
if (!condition_which_should_evaluate_true) {
; // Put breakpoint here
}
然后将断点放在该行内。如果条件评估为 NO,则调用您的断点。