responsesToSelector отправить освобожденному объекту

Я пытаюсь выяснить, почему мое приложение дает сбой (RSS Reader), если я отправляю неправильный URL-адрес в NSXML Parser. Я получил EXC_BAD_ACCESS. Итак, после некоторых поисков я обнаружил, что мне нужно использовать зомби. Поэтому я добавил в среду следующие аргументы:

CFZombieLevel = 3
NSMallocStaclLogging = YES
NSDeallocateZombies = NO
MallocStackLoggingNoCompact = YES
NSZombieEnabled = YES
NSDebugEnabled = YES
NSAutoreleaseFreedObjectCheckEnabled = YES

Я также добавил malloc_error_break в качестве точки останова. Затем я добавил несколько других точек останова в графическом интерфейсе и нажал «Сборка и отладка». В консоли получаю следующее сообщение:

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? Я не могу найти код, который вызывает сбой моего приложения. Единственное, что я знаю, это то, что это должно быть сообщение responsesToSelector. Я добавил точку останова ко всем своим сообщениям responsesToSelector. Их бьют, но приложение не вылетает в этот момент. Я также пытался прокомментировать их, кроме одного, и это также приводит к сбою приложения. Тот, который не был закомментирован, не попал. Где у меня утечка памяти?

Следующая сбивающая с толку вещь заключается в том, что NSXML Parser продолжает свою работу, несмотря на вызов делегата parseErrorOccurred. После двухкратной ошибки приложение вылетает.

Почему Zombies in the Run с инструментом производительности отключен?

Изменить:

Сейчас воспользовался этой инструкцией (не могу постить. извините. защита от спама) у меня все заработало. Что это означает?

@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?


person testing    schedule 28.08.2010    source источник


Ответы (4)


Зомби отключен, поскольку вы используете его с утечками памяти, поскольку все зомби будут сигнализироваться как утечки. Чтобы запустить инструмент «Зомби», вы можете перейти в меню «Инструмент» и выбрать «Файл»> «Создать» и выбрать только инструмент «Зомби». При этом программа остановится, если зомби получит сообщение, и вам будет предоставлена ​​​​ссылка в небольшом всплывающем окне. к этому зомби-объекту и его истории

person rano    schedule 28.08.2010
comment
Спасибо за подсказку! Я понял это, когда читал app/" rel="nofollow noreferrer">corbinstreehouse.com/blog/2007/10/ Я не знал, что Instruments — это отдельная программа, а не часть Xcode. Тем не менее, я не понимаю, почему они предлагают этот пункт меню, если его нельзя использовать. - person testing; 29.08.2010

Где-то вы выделяете XMLParser. Давайте посмотрим этот код. Вы не выпускаете его автоматически, не так ли?

Где-то он выпускается... он назначен свойству? Давайте посмотрим на это определение свойства.

Позже вызывается метод responsesToSelector:, но это может быть любой метод. Дело в том, что ваш XMLParser был выпущен раньше, чем вы предполагали.

person Graham Perks    schedule 29.08.2010
comment
Поэтому я отредактировал свой вопрос. Я использую alloc, поэтому я не думаю, что я его автоматически выпускаю. Я не использую синтаксический анализатор RSS как свойство. Когда пришло время его выпустить? - person testing; 29.08.2010
comment
Похоже, ваш класс XMLParser ожидает ответа асинхронного ввода-вывода? Если это так, он должен висеть до завершения. Поэтому вы не можете выпустить его в loadData. Самопроизвольное освобождение в connectionDidFinishLoading: стоит попробовать, если на этом завершается обработка. Вам придется сделать то же самое в любом методе обработки ошибок соединения. - person Graham Perks; 29.08.2010
comment
Я не просматривал синхронные/асинхронные вещи. Итак, я говорю вам, что я использую. Я использую NSURLConnection и NSXMLParser. NSURLConnection создает асинхронные соединения, а NSXMLParser — это анализатор SAX. Так как я запускаю парсинг в connectionDidFinishLoading: парсинг происходит, когда файл был скачан (неисправный файл - HTML-файл, где происходит перенаправление через 5 секунд). Проект основан на этом руководстве: cocoadevblog.com/iphone- учебник по созданию программы для чтения RSS-каналов. - person testing; 30.08.2010

В 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 и имеет собственный метод установки и получения (я думаю, вы также можете объявить ее как свойство). Второй раз:

- (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 выглядит следующим образом:

В loadData в RootViewController.m я его никогда не выпускаю. К сожалению, если я сделаю это в 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.

Так что я думаю, что оставлю это до тех пор, пока я не столкнусь с новой ошибкой или кто-нибудь из вас не объяснит мне, почему это плохо.

person testing    schedule 30.08.2010

У вас также есть rssParser, та же переменная экземпляра, которую вы установили здесь для выпуска...

- (void)errorOccurred {
    [rssParser release];
    rssParser = nil;
    if ([activityIndicator isAnimating]) {
        [activityIndicator stopAnimating];
    }
}

... выпущено в вашем методе Dealloc? Это вызовет двойное освобождение и, следовательно, EXEC_BAD_ACCESS.

person Kevin Teman    schedule 12.01.2011
comment
Хорошая точка зрения! Этот вопрос слишком старый, я не могу вспомнить. Прости. В моем текущем проекте больше нет релиза в errorOccured. - person testing; 15.01.2011