Почему UITableView не перезагружается (даже в основном потоке)? - PullRequest
1 голос
/ 20 января 2010

У меня есть две программы, которые в основном делают одно и то же. Они читают канал XML и анализируют элементы. Конструкция обеих программ заключается в использовании асинхронного NSURLConnection для получения данных, а затем для создания нового потока для обработки синтаксического анализа. Когда анализируются партии из 5 элементов, он перезванивает в главный поток для перезагрузки UITableView.

Моя проблема в том, что она отлично работает в одной программе, но не в другой. Я знаю, что на самом деле синтаксический анализ происходит в фоновом потоке, и я знаю, что [tableView reloadData] выполняется в основном потоке; однако, он не перезагружает таблицу, пока весь анализ не будет завершен. Я в тупике. Насколько я могу судить ... обе программы структурированы одинаково. Вот код из приложения, который работает некорректно.

- (void)startConnectionWithURL:(NSString *)feedURL feedList:(NSMutableArray *)list {
self.feedList = list;

// Use NSURLConnection to asynchronously download the data. This means the main thread will not be blocked - the
// application will remain responsive to the user. 
//
// IMPORTANT! The main thread of the application should never be blocked! Also, avoid synchronous network access on any thread.
//
NSURLRequest *feedURLRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:feedURL]];
self.bloggerFeedConnection = [[[NSURLConnection alloc] initWithRequest:feedURLRequest delegate:self] autorelease];

// Test the validity of the connection object. The most likely reason for the connection object to be nil is a malformed
// URL, which is a programmatic error easily detected during development. If the URL is more dynamic, then you should
// implement a more flexible validation technique, and be able to both recover from errors and communicate problems
// to the user in an unobtrusive manner.
NSAssert(self.bloggerFeedConnection != nil, @"Failure to create URL connection.");

// Start the status bar network activity indicator. We'll turn it off when the connection finishes or experiences an error.
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
}

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
self.bloggerData = [NSMutableData data];
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
    [bloggerData appendData:data];
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
self.bloggerFeedConnection = nil;
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;   
// Spawn a thread to fetch the link data so that the UI is not blocked while the application parses the XML data.
//
// IMPORTANT! - Don't access UIKit objects on secondary threads.
//
[NSThread detachNewThreadSelector:@selector(parseFeedData:) toTarget:self withObject:bloggerData];
// farkData will be retained by the thread until parseFarkData: has finished executing, so we no longer need
// a reference to it in the main thread.
self.bloggerData = nil;
}

Если вы прочитаете это сверху вниз, вы увидите, когда NSURLConnection закончится, я отсоединяю новый поток и вызываю parseFeedData.

- (void)parseFeedData:(NSData *)data {
// You must create a autorelease pool for all secondary threads.
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

self.currentParseBatch = [NSMutableArray array];
self.currentParsedCharacterData = [NSMutableString string];
self.feedList = [NSMutableArray array];
//
// It's also possible to have NSXMLParser download the data, by passing it a URL, but this is not desirable
// because it gives less control over the network, particularly in responding to connection errors.
//
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data];
[parser setDelegate:self];
[parser parse];

// depending on the total number of links parsed, the last batch might not have been a "full" batch, and thus
// not been part of the regular batch transfer. So, we check the count of the array and, if necessary, send it to the main thread.
if ([self.currentParseBatch count] > 0) {
    [self performSelectorOnMainThread:@selector(addLinksToList:) withObject:self.currentParseBatch waitUntilDone:NO];
}
self.currentParseBatch = nil;
self.currentParsedCharacterData = nil;
[parser release];        
[pool release];
}

В делегате элемента end end я проверяю, что перед вызовом основного потока для выполнения обновления было проанализировано 5 элементов.

- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
if ([elementName isEqualToString:kItemElementName]) {
    [self.currentParseBatch addObject:self.currentItem];
    parsedItemsCounter++;
    if (parsedItemsCounter % kSizeOfItemBatch == 0) {
        [self performSelectorOnMainThread:@selector(addLinksToList:) withObject:self.currentParseBatch waitUntilDone:NO];
        self.currentParseBatch = [NSMutableArray array]; 
    }
}  

// Stop accumulating parsed character data. We won't start again until specific elements begin.
accumulatingParsedCharacterData = NO;
}

- (void)addLinksToList:(NSMutableArray *)links {
[self.feedList addObjectsFromArray:links];
// The table needs to be reloaded to reflect the new content of the list.

if (self.viewDelegate != nil && [self.viewDelegate respondsToSelector:@selector(parser:didParseBatch:)]) {
    [self.viewDelegate parser:self didParseBatch:links];
}

}

Наконец, делегат UIViewController:

- (void)parser:(XMLFeedParser *)parser didParseBatch:(NSMutableArray *)parsedBatch {
NSLog(@"parser:didParseBatch:");
[self.selectedBlogger.feedList addObjectsFromArray:parsedBatch];
[self.tableView reloadData];

}

Если я запишу в журнал, когда мой делегат контроллера представления запускается для перезагрузки таблицы, и когда cellForRowAtIndexPath запускается при перестроении таблицы, тогда журнал выглядит примерно так:

парсер: didParseBatch:
парсер: didParseBatch:
Tableview: cellForRowAtIndexPath
Tableview: cellForRowAtIndexPath
Tableview: cellForRowAtIndexPath
Tableview: cellForRowAtIndexPath
Tableview: cellForRowAtIndexPath
парсер: didParseBatch:
парсер: didParseBatch:
парсер: didParseBatch:
Tableview: cellForRowAtIndexPath
Tableview: cellForRowAtIndexPath
Tableview: cellForRowAtIndexPath
Tableview: cellForRowAtIndexPath
Tableview: cellForRowAtIndexPath
парсер: didParseBatch:
Tableview: cellForRowAtIndexPath
Tableview: cellForRowAtIndexPath
Tableview: cellForRowAtIndexPath
Tableview: cellForRowAtIndexPath
Tableview: cellForRowAtIndexPath
парсер: didParseBatch:
парсер: didParseBatch:
парсер: didParseBatch:
парсер: didParseBatch:
Tableview: cellForRowAtIndexPath
Tableview: cellForRowAtIndexPath
Tableview: cellForRowAtIndexPath
Tableview: cellForRowAtIndexPath
tableView: cellForRowAtIndexPath

Очевидно, что tableView не перезагружается, когда я говорю это каждый раз.

Лог приложения, который работает правильно, выглядит следующим образом:

парсер: didParseBatch:
Tableview: cellForRowAtIndexPath
Tableview: cellForRowAtIndexPath
Tableview: cellForRowAtIndexPath
Tableview: cellForRowAtIndexPath
Tableview: cellForRowAtIndexPath
парсер: didParseBatch:
Tableview: cellForRowAtIndexPath
Tableview: cellForRowAtIndexPath
Tableview: cellForRowAtIndexPath
Tableview: cellForRowAtIndexPath
Tableview: cellForRowAtIndexPath
парсер: didParseBatch:
Tableview: cellForRowAtIndexPath
Tableview: cellForRowAtIndexPath
Tableview: cellForRowAtIndexPath
Tableview: cellForRowAtIndexPath
Tableview: cellForRowAtIndexPath
парсер: didParseBatch:
Tableview: cellForRowAtIndexPath
Tableview: cellForRowAtIndexPath
Tableview: cellForRowAtIndexPath
Tableview: cellForRowAtIndexPath
Tableview: cellForRowAtIndexPath
парсер: didParseBatch:
Tableview: cellForRowAtIndexPath
Tableview: cellForRowAtIndexPath
Tableview: cellForRowAtIndexPath
Tableview: cellForRowAtIndexPath
tableView: cellForRowAtIndexPath

1 Ответ

0 голосов
/ 20 января 2010

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

Таблица вызывает как – numberOfSectionsInTableView:, так и – tableView:numberOfRowsInSection: перед вызовом - tableView:cellForRowAtIndexPath. Если один из них сообщит неправильный номер, табличное представление предположит, что у него нет данных и ничего не делает.

...