5

假设一个可可应用程序必须在退出之前执行一些极其重要的objective-c 操作(出于任何原因——崩溃或退出)。

是否可以确保执行任意 Objective-c 代码以响应每个崩溃事件?(SIGINT, SIGBUS,SIGSEGV等,忽略“无法捕捉的”(SIGSTOP, SIGKILL, 闪电等))


知道你的钩子试图做什么会很有帮助。

例如:假设要运行应用程序,它绝对必须更改一些可变的系统范围配置变量 X 的值。在启动时,应用程序会拍摄 X 的当前状态的快照,然后对其进行修改。当应用程序成功退出时,它只是在终止前将 X 恢复为隐藏的原始值。我的问题是:即使应用程序崩溃,是否有可能确保 X 得到恢复?

4

3 回答 3

6

不,没有办法保证任何给定的代码将在应用程序终止时执行,因为用户可能会以应用程序无法捕获的方式终止应用程序(例如,拔掉桌面上的插头机器)。

您的代码绝对必须针对意外终止进行强化

你需要加强多少取决于你的应用程序。在基于文档的应用程序中(在现代经常自动保存模型之前),在上次保存后丢失数据是意料之中的(而且很烦人)。

对于您的应用程序,您需要平衡持久性成本与持久数据的价值。


我只对可以捕获的终止信号感兴趣。

“可以被抓住”和“可以做任何有用的事情”之间是有区别的。正如您所指出的,可以捕获许多信号。

但是,您真的不能在信号处理程序中做任何事情。从技术上讲,您甚至无法分配内存。

同样,通常会生成信号或异常,因为应用程序已进入未定义的、灾难性损坏的状态。因此,您不能指望应用程序中的任何内部状态都可用。

我见过很多情况,当崩溃发生时,应用程序会尝试保存用户状态,只是为了相当愉快地写入损坏状态,最终导致用户丢失更多数据。

知道你的钩子试图做什么会很有帮助。


有关更多信息,请参阅 sigaction 手册页。

具体来说,可以安全调用的函数列表包括 _exit()、access()、alarm()、cfgetispeed()、cfgetospeed()、cfsetispeed()、cfsetospeed()、chdir()、chmod()、chown( )、close()、creat()、dup()、dup2()、execle()、execve()、fcntl()、fork()、fpathconf()、fstat()、fsync()、getegid()、 geteuid()、getgid()、getgroups()、getpgrp()、getpid()、getppid()、getuid()、kill()、link()、lseek()、mkdir()、mkfifo()、open( ), pathconf(), pause(), pipe(), raise(), read(), rename(), rmdir(), setgid(), setpgid(), setsid(), setuid(), sigaction(), sigaddset(), sigdelset(), sigemptyset(), sigfillset(), sigismember(), signal(), sigpending(), sigprocmask(), sigsuspend(), sleep(), stat(), sysconf(), tcdrain( )、tcflow()、tcflush()、tcgetattr()、tcgetpgrp()、tcsendbreak()、tcsetattr()、tcsetpgrp()、time()、times()、umask()、uname()、unlink()、时间(),wait()、waitpid()、write()、aio_error()、sigpause()、aio_return()、aio_suspend()、sem_post()、sigset()、strcpy()、strcat()、strncpy()、strncat( )、strlcpy() 和 strlcat()。

上述列表中没有的所有函数都被认为对信号不安全。也就是说,从信号处理程序调用此类函数时的行为是未定义的。但总的来说,信号处理程序应该做的只是设置一个标志。大多数其他操作都不安全。

翻译:你真的不能在信号处理程序中做很多有用的事情。

Mpte a;sp tjat sogma; ,au ne de;overed pm amu tjread amd ,ogjt ne de;overed nu 中断当前正在执行的函数。

我的意思是:请记住,信号处理程序可能会在随机线程上传递,也可能会中断当前正在执行的函数。即您的应用程序的状态可能有些不确定。

于 2012-09-30T18:46:38.543 回答
3

最简洁的答案是不。

更长的答案仍然是否定的,但您可以最大限度地减少无法重置系统范围的可变配置变量的机会。概述:

  • 在启动时让您的应用程序产生一个不露面的后台进程;
  • 不露面的后台进程应该改变你的变量,然后等待(wait(2)和朋友一样)直到它的父进程过期;
  • 在其父级到期时,它会重置变量,然后自行到期。

您的不露面后台应用程序应该简短而简单,因此希望是健壮的。它远非万无一失,或者对于那些想杀婴的用户来说是安全的,但它缩小了阻止变量重置的机会。

于 2012-09-30T21:21:28.133 回答
1

如果要捕获信号,可以使用信号处理程序。

    signal(SIGBUS, signalHandler);
    signal(SIGSEGV, signalHandler);

void signalHandler(int signal)
{   
    NSMutableString *crashReport = [[NSMutableString alloc] init];
    void* callstack[128];
    int i, frames = backtrace(callstack, 128);
    char** strs = backtrace_symbols(callstack, frames);
    for (i = 0; i < frames; ++i) {
        //printf("%s\n", strs[i]);
        [crashReport appendFormat:@"%s\n",strs[i]];

    }
    free(strs);
    // write crashReport to file
    [crashReport release];
    exit(1);
}  

但如果崩溃发生在 NSMutableString alloc 或 release 或 free() 函数中,则此代码将不起作用。(正如bbum所解释的:
无法保证任何给定的代码将在应用程序终止时执行)

于 2012-10-01T05:25:12.437 回答