2010-01-20 5 views
1

J'ai deux programmes qui font essentiellement la même chose. Ils lisent un flux XML et analysent les éléments. La conception des deux programmes est d'utiliser un NSURLConnection asynchrone pour obtenir les données puis de lancer un nouveau thread pour gérer l'analyse syntaxique. Lorsque des lots de 5 éléments sont analysés, il rappelle le thread principal pour recharger UITableView.Pourquoi UITableView ne rechargeait (même sur le thread principal)?

Mon problème est-il fonctionne très bien dans un programme, mais pas l'autre. Je sais que l'analyse syntaxique est en fait sur le fil INTERVENUES de fond et je sais que [tableView reloadData] est en cours d'exécution sur le thread principal; cependant, il ne recharge pas la table tant que l'analyse n'est pas terminée. Je suis perplexe. Pour autant que je sache ... les deux programmes sont structurés exactement de la même manière. Voici un code de l'application qui ne fonctionne pas correctement.

- (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; 
} 

Si vous lisez ceci de haut en bas, vous pouvez voir quand le NSURLConnection est terminé, je détachez un nouveau thread et appelle 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]; 
} 

Dans le délégué de l'élément final ai-je vérifier pour voir que 5 éléments ont été analysés avant d'appeler le thread principal pour effectuer la mise à jour.

- (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]; 
} 

} 

Enfin, le délégué UIViewController:

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

} 

Si j'écris au journal lorsque mon point de vue les feux de délégué du contrôleur pour recharger la table et quand cellForRowAtIndexPath feux comme il est la reconstruction de la table, alors le journal ressemble à quelque chose comme ceci:

analyseur

: didParseBatch:
analyseur: didParseBatch:
tableView: cellForRowAtIndexPath
tableView: cellForRowAtIndexPath
tableView: cellForRowAtIndexPath
tableView: cellForRowAtIndexPath
tableView: cellForRowAtIndexPath
analyseur: didParseBatch:
analyseur: didParseBatch:
analyseur: didParseBatch:
tableView: cellForRowAtIndexPath
tableView: cellForRowAtIndexPath
tableView: cellForRowAtIndexPath
tableView: cellForRowAtIndexPath
tableView: cellForRowAtIndexPath
analyseur: didParseBatch:
tableView: cellForRowAtIndexPath
tableView: cellForRowAtIndexPath
tableView: cellForRowAtIndexPath
tableView: cellForRowAtIndexPath
tableView: analyseur cellForRowAtIndexPath
: didParseBatch:
analyseur: didParseBatch:
analyseur : didParseBatch:
analyseur: didParseBatch:
tableView: cellForRowAtIndexPath
tableView: cellForRowAtIndexPath
tableView: cellForRowAtIndexPath
tableView: cellForRowAtIndexPath
tableView: cellForRowAtIndexPath

De toute évidence, le tableView ne rechargeait quand je le dis à chaque fois.

Le journal de l'application qui fonctionne correctement ressemble à ceci:

analyseur

: didParseBatch:
tableView: cellForRowAtIndexPath
tableView: cellForRowAtIndexPath
tableView: cellForRowAtIndexPath
tableView: cellForRowAtIndexPath
tableView: cellForRowAtIndexPath
parseur: didParseBatch:
tableView: cellForRowAtIndexPath
tableView: cellForRowAtIndexPath
tableView: cellForRowAtIndexPath
tableView: cellForRowAtIndexPath
tableView: cellForRowAtIndexPath
analyseur: didParseBatch:
tableView: cellForRowAtIndexPath
tableView: cellForRowAtIndexPath
tableView: cellForRowAtIndexPath
tableView: cellForRowAtIndexPath
tableView: cellForRowAtIndexPath
analyseur: didParseBatch:
tableView: cellForRowAtIndexPath
tableView: cellForRowAtIndexPath
tableView: cellForRowAtIndexPath
tableView: cellForRowAtIndexPath
tableView: cellForRowAtIndexPath
analyseur: didParseBatch:
tableView: cellForRowAtIndexPath
tableView: cellForRowAtIndexPath
tableView: cellForRowAtIndexPath
tableView: cellForRowAtIndexPath
tableView: cellForRowAtIndexPath

+0

Avez-vous déjà résolu ce problème? – bentford

Répondre

0

Je vais deviner que quelque chose dans votre modèle de données est à blâmer. Très probablement, je pense que ce serait quelque chose qui ferait que le numéro de section serait erroné ou qui indiquerait zéro rangée pour une section. Une table appelle à la fois – numberOfSectionsInTableView: et – tableView:numberOfRowsInSection: avant d'appeler - tableView:cellForRowAtIndexPath. Si l'un d'entre eux signale un mauvais numéro, la vue de table supposera qu'il n'a aucune donnée et ne fait rien.

+0

Je ne pense pas que cette théorie soit correcte. Le nombre de sections est codé en dur selon 1. Voir ci-dessous pour plus de détails. analyseur: didParseBatch: tableView: numberOfRowsInSection: 5 analyseur: didParseBatch: tableView: numberOfRowsInSection: 10 tableView: cellForRowAtIndexPath tableView: cellForRowAtIndexPath tableView: cellForRowAtIndexPath tableView: cellForRowAtIndexPath tableView: cellForRowAtIndexPath analyseur: didParseBatch: tableView : numberOfRowsInSection: 15 analyseur: didParseBatch: tableView: numberOfRowsInSection: 20 analyseur: didParseBatch: tableView: numberOfRowsInSection: 25 – radesix

+0

Eh bien je suis o ut d'idées.À ma connaissance, la seule raison pour laquelle reload ne provoquerait pas l'appel de 'tableView: cellForRowAtIndexPath' serait si la table ne pensait pas qu'elle était nécessaire et que seule la source de données pouvait la déterminer. – TechZen

Questions connexes