Objective-C 线程安全代码以防止崩溃

发布于 2025-01-07 04:26:18 字数 1465 浏览 1 评论 0原文

我收到了带有 SIGSEGV 的 iPhone 崩溃报告,我想我已经缩小了可能的原因和解决方案的范围。 由于线程引起的崩溃很难调试,我无法重现这个问题,但可以为我的假设提供一些帮助 - 它合理吗?

我的代码使用 ASIHttpRequest 通过 ASINetWorkQueue 下载一组文件。这是一个简化的示例

//initialize download queue and do this code block in a loop for each file

NSURL *fileURL = [NSURL URLWithString:...
__block ASIHTTPRequest *fileRequest = [ASIHTTPRequest requestWithURL:fileURL];

[fileRequest setCompletionBlock:^{
   //do some stuff   
}];
[fileRequest setFailedBlock:^{
    NSString *someError = [NSString stringWithFormat:...
    [self someErrorMethod:someError];       
}];

[downloadQueue addOperation:...

-(void)someErrorMethod(NSString *errorMessage) {
    DDLogWarn(errorMessage);

    if ([self downloadQueue]) {
        for (ASIHTTPRequest *request in [[self downloadQueue] operations]) {
            [request clearDelegatesAndCancel];
        }
        [[self downloadQueue] reset];
    }
 }

崩溃报告的前 2 行是

  • libobjc.A.dylib 0x31846fbc objc_msgSend + 15
  • MyApp 0x0002cab5 -[Myapp someErrorMethod:] (MyApp.m:)

我对为什么会发生这种情况的思考

  • 文件下载失败并且失败block 被调用
  • 它会遍历每个请求并清除委托并取消它们,然后重置队列
  • 但是,在它运行之前,另一个文件下载失败并进入失败块回调
  • 但是,由于它现在已被取消,因此其失败块已被释放
  • 当代码尝试记录错误消息时,其内存已被释放并且随之而来的是不可预测的结果

这有意义吗?由于我是 Objective-C 的新手,我的分析正确还是我遗漏了一些明显的东西?

我正在考虑使用锁来使 errorMethod 线程安全,希望它能解决这个问题。根据上面的代码,这听起来像是正确的解决方案吗?

谢谢

I got an iPhone crash report with a SIGSEGV and I think I've narrowed down the possible cause and a solution.
Since crashes caused by threads are hard to debug I can't repro this problem, but could use some help with my hypothesis - is it sound?

My code uses ASIHttpRequest to download a set of files using an ASINetWorkQueue. Here is a simplified sample

//initialize download queue and do this code block in a loop for each file

NSURL *fileURL = [NSURL URLWithString:...
__block ASIHTTPRequest *fileRequest = [ASIHTTPRequest requestWithURL:fileURL];

[fileRequest setCompletionBlock:^{
   //do some stuff   
}];
[fileRequest setFailedBlock:^{
    NSString *someError = [NSString stringWithFormat:...
    [self someErrorMethod:someError];       
}];

[downloadQueue addOperation:...

-(void)someErrorMethod(NSString *errorMessage) {
    DDLogWarn(errorMessage);

    if ([self downloadQueue]) {
        for (ASIHTTPRequest *request in [[self downloadQueue] operations]) {
            [request clearDelegatesAndCancel];
        }
        [[self downloadQueue] reset];
    }
 }

The top 2 lines of the crash report are

  • libobjc.A.dylib 0x31846fbc objc_msgSend + 15
  • MyApp 0x0002cab5 -[Myapp someErrorMethod:] (MyApp.m:)

My thinking for why this happened

  • A file download fails and the failed block is called
  • It goes through every request and clears delegates and cancels them and then resets the queue
  • However, while it is till running, another file download fails and enters the failed block callback
  • However, since it has now been cancelled, its failed block has been released
  • When the code tries to log the error message, its memory has been released and unpredictable results follow

Does this make sense? Since I am newish to Objective-C, is my analysis correct or am I missing something obvious?

I am thinking of using a lock to make the errorMethod thread safe with the hope that it will fix this issue. Does that sound like the right solution based on the code above?

Thanks

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

千柳 2025-01-14 04:26:18

这听起来不太可能。 ASIHttpRequest 可能在同一线程上执行所有回调(我对此相当确定)。

如果我不得不猜测,您的错误更有可能出现在这一行中:

DDLogWarn(errorMessage);

DDLogWarn 的第一个参数是格式,而不是字符串。在 errorMessage 包含 % 的任何情况下,这都可能会崩溃。您的意思是:

DDLogWarn(@"%@", errorMessage);

由于 DDLogWarn() 是一个 varags 方法,因此它将开始用它在堆栈上找到的(随机)值替换字符串中的任何 % 替换。它将读取堆栈,直到用完 % 替换为止。如果任何 % 替换是基于指针的(如 %s 或 %@),那么它将跟随指针到达随机位置。

SEG_ACCERR 意味着您请求了一块不属于您的内存。 SEG_MAPERR 意味着您请求了一块未映射的内存。两者都是遵循完全随机指针的预期结果。

This doesn't sound likely. ASIHttpRequest likely performs all of its callbacks on the same thread (I'm fairly certain on this one).

If I had to guess, your error is more likely in this line:

DDLogWarn(errorMessage);

The first parameter to DDLogWarn is a format, not a string. This will likely crash in any case that errorMessage includes a %. What you meant is:

DDLogWarn(@"%@", errorMessage);

Since DDLogWarn() is a varags method, it will start substituting the (random) values it finds on the stack for any % substitutions in the string. It will read the stack until you run out of % substitutions. If any of the % substitutions are pointer-based (like %s or %@), then it will follow the pointer to a random location.

SEG_ACCERR means that you've requested a piece of memory you don't own. SEG_MAPERR means you've requested a piece of memory that is not mapped. Either is an expected result of following a totally random pointer.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文