10

我的应用程序与 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

4 回答 4

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.

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

谢谢。

于 2011-10-06T14:21:06.170 回答
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()));
}

使用Instruments运行您的应用程序,确保每 1 或 2 秒更改一次内存验证。速度慢,但您仍希望在尽可能接近实际内存问题的情况下获得通知。

于 2011-10-06T09:39:32.373 回答
1

查看您的代码:“完成”变量从何而来,谁更改了它的值以及何时更改?现在它看起来很神奇。

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

于 2011-10-06T10:15:09.203 回答
0

只是我的一点贡献。

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

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

在 iOS4 中,您需要 NSAutoreleasePool,但在 iOS5 中不需要。

于 2011-10-12T16:39:44.303 回答