如果我向NSXML Parser发送错误的网址,我会尝试找出我的应用崩溃的原因(RSS阅读器)。我得到了EXC_BAD_ACCES
S。所以经过一些搜索我发现我必须使用Zombies。所以我在环境中添加了以下参数:
CFZombieLevel = 3
NSMallocStaclLogging = YES
NSDeallocateZombies = NO
MallocStackLoggingNoCompact = YES
NSZombieEnabled = YES
NSDebugEnabled = YES
NSAutoreleaseFreedObjectCheckEnabled = YES
我还添加了malloc_error_break
作为断点。然后我在GUI中添加了一些其他断点并按下了Build和Debug。在控制台中,我收到以下消息:
2010-08-28 18:41:49.761 RssReader[2850:207] *** -[XMLParser respondsToSelector:]: message sent to deallocated instance 0x59708e0
有时我也会收到以下消息:
wait_fences: failed to receive reply: 10004003
如果我输入“shell malloc_history 2850 0x59708e0”,我会得到以下内容:
...
ALLOC 0x5970870-0x59709d7 [size=360]: thread_a0aaa500 |start | main | UIApplicationMain | -[UIApplication _run] | CFRunLoopRunInMode | CFRunLoopRunSpecific | __CFRunLoopRun | __CFRunLoopDoSource1 | __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ | PurpleEventCallback | _UIApplicationHandleEvent | -[UIApplication sendEvent:] | -[UIApplication handleEvent:withNewEvent:] | -[UIApplication _runWithURL:payload:launchOrientation:statusBarStyle:statusBarHidden:] | -[UIApplication
...
----
FREE 0x5970870-0x59709d7 [size=360]: thread_a0aaa500 |start | main | UIApplicationMain | -[UIApplication _run] | CFRunLoopRunInMode | CFRunLoopRunSpecific | __CFRunLoopRun | __CFRunLoopDoSource1 | __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ | PurpleEventCallback | _UIApplicationHandleEvent | -[UIApplication sendEvent:] | -[UIApplication handleEvent:withNewEvent:] | -[UIApplication
...
ALLOC 0x59708e0-0x597090f [size=48]: thread_a0aaa500 |start | main | UIApplicationMain | -[UIApplication _run] | CFRunLoopRunInMode | CFRunLoopRunSpecific | __CFRunLoopRun | __CFRunLoopDoSource1 | __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ | PurpleEventCallback | _UIApplicationHandleEvent | -[UIApplication sendEvent:] | -[UIApplication handleEvent:withNewEvent:] | -[UIApplication _runWithURL:payload:launchOrientation:statusBarStyle:statusBarHidden:] | -[UIApplication
...
Binary Images:
0x1000 - 0x6ff3 +RssReader ??? (???) <6EBB16BC-2BCE-CA3E-C76E-F0B078995E2D> /Users/svp/Library/Application Support/iPhone Simulator/4.0.1/Applications/AF4CE7CA-88B6-44D4-92A1-F634DE7B9072/RssReader.app/RssReader
0xe000 - 0x1cfff3 +Foundation 751.32.0 (compatibility 300.0.0) <18F9E1F7-27C6-2B64-5B9D-BAD16EE5227A>
...
这是什么意思?我怎么知道0x59708e0是哪个对象?我找不到导致我的应用崩溃的代码。我唯一知道的是它应该是一个respondsToSelector消息。我在所有的respondsToSelector消息中添加了一个断点。他们被击中,但应用程序在那时没有崩溃。我还尝试将它们评论出来,除了一个并且还让应用程序崩溃。未被评论的那个没有被击中。我在哪里有内存泄漏?
下一个令人困惑的事情是NSXML Parser继续其工作,尽管调用了parseErrorOccurred委托。两次错误后,应用程序崩溃。
为什么僵尸在Run with Peformance Tool中被禁用?
修改
现在我使用了这个指令(无法发布。抱歉。垃圾邮件预防)我得到了这个工作。这是什么意思?
@Graham:
在我的解析器类中,我实例化了NSXMLParser
:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
...
NSXMLParser *rssParser = [[NSXMLParser alloc] initWithData:responseData];
[rssParser setDelegate:self];
...
[rssParser parse];
//[rssParser release];
}
在我搜索错误期间,我注释了释放方法。目前rssParser永远不会在解析器类中发布。
在我的RootViewController
类中,我实例化了我的解析器:
- (void)loadData {
if (newsItems == nil) {
[activityIndicator startAnimating];
XMLParser *rssParser = [[XMLParser alloc] init];
[rssParser parseRssFeed:@"http://feeds2.feedburner.com/TheMdnShowtest" withDelegate:self];
[rssParser release];
rssParser = nil;
} else {
[self.tableView reloadData];
}
}
如果我不在这里发布它,它不会崩溃。但是对于每个alloc,我必须发布一个版本?或者我应该在NSXMLParser
中自动发布connectionDidFinishLoading
吗?
答案 0 :(得分:1)
当你使用Memory Leaks时,Zombie被禁用,因为所有Zombies都会被标记为泄漏。要运行Zombie工具,您可以转到“仪器”菜单并执行“文件”&gt;“新建”并单独选择“僵尸”工具,这样,如果僵尸收到消息,程序将停止,并且您将在弹出的小弹出窗口中获得链接到那个僵尸对象及其历史
答案 1 :(得分:1)
你正在分配XMLParser。我们来看看那段代码。你不是自动释放它,是吗?
某个地方它正在被释放......它被分配给一个财产吗?让我们看看属性定义。
稍后调用respondsToSelector:方法,但这可能是任何方法。关键是你的XMLParser在你预期之前就已经发布了。
答案 2 :(得分:1)
在RootViewController.h中我声明了属性rssParser:
@class XMLParser;
@interface RootViewController : UITableViewController {
...
XMLParser *rssParser;
}
...
@property (retain, nonatomic) XMLParser *rssParser;
@end
在RootViewController.m中,我有一个名为errorOccurred的方法:
- (void)errorOccurred {
[rssParser release];
rssParser = nil;
if ([activityIndicator isAnimating]) {
[activityIndicator stopAnimating];
}
}
在我的XMLParser.m文件中,我调用了两次errorOccurred:
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
...
if ([_delegate respondsToSelector:@selector(errorOccurred)])
[_delegate errorOccurred];
else
{
[NSException raise:NSInternalInconsistencyException
format:@"Delegate doesn't respond to errorOccurred:"];
}
}
要了解如何声明_delegate,请查看教程http://www.cocoadevblog.com/iphone-tutorial-creating-a-rss-feed-reader。它是一个id变量,并且有一个自己的setter和getter方法(你也可以将它声明为属性)。第二次:
- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError {
...
if ([_delegate respondsToSelector:@selector(errorOccurred)])
[_delegate errorOccurred];
else
{
[NSException raise:NSInternalInconsistencyException
format:@"Delegate doesn't respond to errorOccurred:"];
}
}
我的rssParser变量的发布如下:
在RootViewController.m中的loadData中,我从不发布它。不幸的是,如果我在loadData中执行它会崩溃。只有在发生错误(见上文)或dealloc方法时才会释放它。但我认为这应该可以正常工作,因为它被宣布为财产。
- (void)loadData {
if (newsItems == nil) {
[activityIndicator startAnimating];
self.rssParser = [[XMLParser alloc] init];
[rssParser parseRssFeed:@"http://www.wrongurl.com/wrongrss.xml" withDelegate:self];
} else {
[self.tableView reloadData];
}
}
在XMLParser.m中,我在解析方法之后发布它:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
...
NSXMLParser *rssParser = [[NSXMLParser alloc] initWithData:responseData];
[rssParser setDelegate:self];
[rssParser parse];
[rssParser release];
rssParser = nil;
}
请注意,两个变量名称相同(rssParser),但它们不同。在RootViewController中,我正在创建XMLParser的实例,在XMLParser.m中,我正在创建NSXMLParser的实例。
所以我想我会留下它,直到我没有遇到新的错误,或者有人向你解释为什么这很糟糕。
答案 3 :(得分:1)
你是否也有rssParser,你设置为在此发布的相同实例变量......
- (void)errorOccurred {
[rssParser release];
rssParser = nil;
if ([activityIndicator isAnimating]) {
[activityIndicator stopAnimating];
}
}
...在你的dealloc方法中发布了吗?这将导致双重释放,从而导致EXEC_BAD_ACCESS。