iOS5在runMode:beforeDate期间崩溃:

时间:2011-10-06 09:19:39

标签: objective-c ios exc-bad-access ios5 nsrunloop

我的应用程序与iOS5 b7和GM版本的兼容性存在问题。

问题出现在下一行代码中:

do {
    [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
} while (!done);

在一些迭代后,应用程序与信号EXC_BAD_ACCESS崩溃。

传递的迭代次数是随机的(从2到7)。

在iOS4和iOS3上,一切都运行良好。

Apple的示例中出现了同样的问题:XMLPerformance Sample

您如何看待这个?

10月12日我的应用程序的数千名用户将升级到iOS5,我不希望我的应用程序在AppStore中出现如此奇怪的错误。

4 个答案:

答案 0 :(得分:10)

过了4个小时,我发现了问题。我将在XMLPerformance sample中描述我是如何解决问题的。

问题出在NSAutoreleasePool。有@property (nonatomic, assign) NSAutoreleasePool *downloadAndParsePool;。当应用程序开始下载Top300 Paid Apps RSS时,使用[NSThread detachNewThreadSelector:@selector(downloadAndParse:) toTarget:self withObject:url];创建新线程。所以在那个线程中我们应该保留本地自动释放池。它以下一种方式完成:

- (void)downloadAndParse:(NSURL *)url {
    self.downloadAndParsePool = [[NSAutoreleasePool alloc] init];

    // initializing internet connection and libxml parser.
    if (rssConnection != nil) {
         do {
             [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
         } while (!done);
    }
    // Release resources used only in this thread.
    [downloadAndParsePool release]; 
    self.downloadAndParsePool = nil;
}

所以在downloadAndParse:一切都很好。现在让我们看一下在解析RSS中的项时调用的一个方法:

- (void)finishedCurrentSong {
    // sending new item to delegate and other ...
    countOfParsedSongs++;
    // Periodically purge the autorelease pool. The frequency of this action may need to be tuned according to the 
    // size of the objects being parsed. The goal is to keep the autorelease pool from growing too large, but 
    // taking this action too frequently would be wasteful and reduce performance.
    if (countOfParsedSongs == kAutoreleasePoolPurgeFrequency) {
        [downloadAndParsePool release];
        self.downloadAndParsePool = [[NSAutoreleasePool alloc] init];
        countOfParsedSongs = 0;
    }
}

如你所见:

[downloadAndParsePool release];
self.downloadAndParsePool = [[NSAutoreleasePool alloc] init];

确切地说,这些行会导致异常。如果我评论他们一切都很好。

但我决定不仅要对这些行进行评论,还要将NSAutoreleasePool中的- (void)downloadAndParse:(NSURL *)url替换为@autorelease块,因为据说它更有效:

- (void)downloadAndParse:(NSURL *)url {
@autoreleasepool {

    // initializing internet connection and libxml parser.
    if (rssConnection != nil) {
         do {
             [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
         } while (!done);
    }
    // Release resources used only in this thread.
    }
}

现在一切正常。我没有解决的唯一问题是:

// Periodically purge the autorelease pool. The frequency of this action may need to be tuned according to the 
// size of the objects being parsed. The goal is to keep the autorelease pool from growing too large, but 
// taking this action too frequently would be wasteful and reduce performance.

因此,如果有人对此问题有任何想法,可以发布另一个答案,并可能尝试更正确地解释错误修复。我很乐意接受这个答案。

感谢。

答案 1 :(得分:2)

这看起来像内存问题,请查看Apple Technote QA1367“Finding EXC_BAD_ACCESS bugs in a Cocoa project

在您的代码中,请尝试尽快崩溃

[item release], item = nil;

它没有解决问题,只是让崩溃发生得更早,希望能给你一个更有意义的学习问题。

如果您正在使用多线程,那么......您可以尝试将“当前”线程ID打印到控制台中,以验证所有内容是否都在您期望它们的线程中运行运行。特别要验证所有UI内容都在主线程中,即使这些代码作为其他代码的副作用运行(可能是错误弹出窗口)。

#include <pthread.h>
- (void)myFunction
{
    NSLog(@"Thread (%d)",
        pthread_mach_thread_np(pthread_self()));
}

使用乐器运行您的应用,确保每1或2秒更改一次内存验证。慢,但你想再次收到尽可能接近实际内存问题的通知。

答案 2 :(得分:1)

查看您的代码:“完成”变量来自哪里,谁改变了它的价值?何时?现在看起来很神奇。

您还可以检查runMode:beforeDate 的返回值,以确保它已运行。如果返回值为NO,则根本不运行runLoop。也许你的代码的其他部分无法处理这种情况?

答案 3 :(得分:0)

只是我的小贡献。

由于我遇到了同样的问题,我发现在iOS5中,您不需要在线程中使用自己的NSAutoreleasePool(由performSelectorOnMainThread使用)。

然后,在你的代码中(xml解析器 - 和我一样),我认为你必须将代码与iOS4和iOS5分开。

使用iOS4,您需要NSAutoreleasePool,但不需要iOS5。