UITableView 不显示解析的数据

Posted

技术标签:

【中文标题】UITableView 不显示解析的数据【英文标题】:UITableView not displaying parsed data 【发布时间】:2010-04-06 04:46:06 【问题描述】:

我有一个 UITableView,它在 Interface Builder 中设置,并在 Xcode 中正确连接到它的类。我还有一个“Importer”类,它下载和解析 RSS 提要并将信息存储在 NSMutableArray 中。

但是我已经验证解析工作正常(使用断点和 NSlog),但 UITable 视图中没有显示任何数据。关于问题可能是什么的任何想法?我几乎没有他们了。

它基于 Apple 的 XML 性能示例。

更新 2: 好的,在完成以下信息后,这就是我所知道的:

没有调用 didParseIncidents:(NSArray *)parsedIncidents,但请继续阅读以了解原因。 当我启动应用程序并重新加载数据时,(void)parsedIncident:(Incident *)incident 运行,但在 if (self.delegate != nil && [self.delegate respondsToSelector:@selector(解析器:didParseIncidents:)]) [self.delegate parser:self didParseIncidents:parsedIncidents]; 我怀疑我的表格问题来自上述代码无法正常工作的事实 - 在示例中 if (self.delegate != nil && [self.delegate respondsToSelector:@selector(parser:didParseIncidents: )]) [self.delegate 解析器:self didParseIncidents:parsedIncidents];运行 在此处执行 po parsedIncidents 会导致“无法访问内存 0x0”错误,而在示例应用程序中会给出 NS 数组 事实上,似乎 parsedIncidents 计数为 0 - 但无法找出原因。

更新: 好的,结果如下:

parserDidEndParsingData 断点上的“po 事件”给了我“无法访问地址 0x0 的内存”的回复。

po [self tableView] 显示 "> 当前语言:自动;目前是objective-c”

在分段行数下的断点中,我得到了以下“()”消息。

我检查的IB项目都如你所说。我怀疑数组没有从上述错误中加载?

如果有帮助,我还添加了解析代码。

这是 TableView.h 的代码:

#import <UIKit/UIKit.h>
#import "IncidentsImporter.h"

@class SongDetailsController;

@interface CurrentIncidentsTableViewController : UITableViewController <IncidentsImporterDelegate> 
    NSMutableArray *incidents;
    SongDetailsController *detailController;
    UITableView *ctableView;
    IncidentsImporter *parser;


@property (nonatomic, retain) NSMutableArray *incidents;
@property (nonatomic, retain, readonly) SongDetailsController *detailController;
@property (nonatomic, retain) IncidentsImporter *parser;
@property (nonatomic, retain) IBOutlet UITableView *ctableView;

// Called by the ParserChoiceViewController based on the selected parser type.
- (void)beginParsing;

@end

还有 .m 的代码:

#import "CurrentIncidentsTableViewController.h"
#import "SongDetailsController.h"
#import "Incident.h"

@implementation CurrentIncidentsTableViewController

@synthesize ctableView, incidents, parser, detailController;

#pragma mark -
#pragma mark View lifecycle


 - (void)viewDidLoad 
    [super viewDidLoad];

     self.parser = [[IncidentsImporter alloc] init];      
     parser.delegate = self;
     [parser start];

     UIBarButtonItem *refreshButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemRefresh target:self action:@selector(beginParsing)];
     self.navigationItem.rightBarButtonItem = refreshButton;
     [refreshButton release];

 // Uncomment the following line to preserve selection between presentations.
 //self.clearsSelectionOnViewWillAppear = NO;

 // Uncomment the following line to display an Edit button in the navigation bar for this view controller.
 // self.navigationItem.rightBarButtonItem = self.editButtonItem;
 

 - (void)viewWillAppear:(BOOL)animated 
     NSIndexPath *selectedRowIndexPath = [ctableView indexPathForSelectedRow];
     if (selectedRowIndexPath != nil) 
         [ctableView deselectRowAtIndexPath:selectedRowIndexPath animated:NO];
     
 

// This method will be called repeatedly - once each time the user choses to parse.
- (void)beginParsing 
    NSLog(@"Parsing has begun");
    //self.navigationItem.rightBarButtonItem.enabled = NO;
    // Allocate the array for song storage, or empty the results of previous parses
    if (incidents == nil) 
        NSLog(@"Grabbing array");
        self.incidents = [NSMutableArray array];
     else 
        [incidents removeAllObjects];
        [ctableView reloadData];
    
    // Create the parser, set its delegate, and start it.
    self.parser = [[IncidentsImporter alloc] init];      
    parser.delegate = self;
    [parser start];


/*
 - (void)viewDidAppear:(BOOL)animated 
 [super viewDidAppear:animated];
 
 */
/*
 - (void)viewWillDisappear:(BOOL)animated 
 [super viewWillDisappear:animated];
 
 */
/*
 - (void)viewDidDisappear:(BOOL)animated 
 [super viewDidDisappear:animated];
 
 */


- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation 
    // Override to allow orientations other than the default portrait orientation.
    return YES;



#pragma mark -
#pragma mark Table view data source

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView 
    // Return the number of sections.
    return 1;



- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 
    // Return the number of rows in the section.
    return [incidents count];



// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 
    NSLog(@"Table Cell Sought");

    static NSString *kCellIdentifier = @"MyCell";
    UITableViewCell *cell = [ctableView dequeueReusableCellWithIdentifier:kCellIdentifier];
    if (cell == nil) 
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:kCellIdentifier] autorelease];
        cell.textLabel.font = [UIFont boldSystemFontOfSize:14.0];
        cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
    
    cell.textLabel.text = @"Test";//[[incidents objectAtIndex:indexPath.row] title];
    return cell;



/*
 // Override to support conditional editing of the table view.
 - (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath 
 // Return NO if you do not want the specified item to be editable.
 return YES;
 
 */


/*
 // Override to support editing the table view.
 - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath 

 if (editingStyle == UITableViewCellEditingStyleDelete) 
 // Delete the row from the data source
 [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:YES];
    
 else if (editingStyle == UITableViewCellEditingStyleInsert) 
 // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
    
 
 */


/*
 // Override to support rearranging the table view.
 - (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath 
 
 */


/*
 // Override to support conditional rearranging of the table view.
 - (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath 
 // Return NO if you do not want the item to be re-orderable.
 return YES;
 
 */


#pragma mark -
#pragma mark Table view delegate

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 
    self.detailController.incident = [incidents objectAtIndex:indexPath.row];
    [self.navigationController pushViewController:self.detailController animated:YES];



#pragma mark -
#pragma mark Memory management

- (void)didReceiveMemoryWarning 
    // Releases the view if it doesn't have a superview.
    [super didReceiveMemoryWarning];

    // Relinquish ownership any cached data, images, etc that aren't in use.


- (void)viewDidUnload 
    // Relinquish ownership of anything that can be recreated in viewDidLoad or on demand.
    // For example: self.myOutlet = nil;


- (void)parserDidEndParsingData:(IncidentsImporter *)parser 
    [ctableView reloadData];
    self.navigationItem.rightBarButtonItem.enabled = YES;
    self.parser = nil;


- (void)parser:(IncidentsImporter *)parser didParseIncidents:(NSArray *)parsedIncidents 
    //[incidents addObjectsFromArray: parsedIncidents];
    // Three scroll view properties are checked to keep the user interface smooth during parse. When new objects are delivered by the parser, the table view is reloaded to display them. If the table is reloaded while the user is scrolling, this can result in eratic behavior. dragging, tracking, and decelerating can be checked for this purpose. When the parser finishes, reloadData will be called in parserDidEndParsingData:, guaranteeing that all data will ultimately be displayed even if reloadData is not called in this method because of user interaction.
    if (!ctableView.dragging && !ctableView.tracking && !ctableView.decelerating) 
        self.title = [NSString stringWithFormat:NSLocalizedString(@"Top %d Songs", @"Top Songs format"), [parsedIncidents count]];
        [ctableView reloadData];
    


- (void)parser:(IncidentsImporter *)parser didFailWithError:(NSError *)error 
    // handle errors as appropriate to your application...



- (void)dealloc 
    [super dealloc];



@end

这是 parser.h 的代码:

#import <UIKit/UIKit.h>
#import <libxml/tree.h>

@class IncidentsImporter, Incident;

// Protocol for the parser to communicate with its delegate.
@protocol IncidentsImporterDelegate <NSObject>

@optional
// Called by the parser when parsing is finished.
- (void)parserDidEndParsingData:(IncidentsImporter *)parser;
// Called by the parser in the case of an error.
- (void)parser:(IncidentsImporter *)parser didFailWithError:(NSError *)error;
// Called by the parser when one or more songs have been parsed. This method may be called multiple times.
- (void)parser:(IncidentsImporter *)parser didParseIncidents:(NSArray *)parsedIncidents;

@end

// This approach to parsing uses NSURLConnection to asychronously retrieve the XML data. libxml's SAX parsing supports chunked parsing, with no requirement for the chunks to be discrete blocks of well formed XML. The primary purpose of this class is to start the download, configure the parser with a set of C callback functions, and pass downloaded data to it. In addition, the class maintains a number of state variables for the parsing.
@interface IncidentsImporter : NSObject 
   @private
    id <IncidentsImporterDelegate> delegate;
    // Reference to the libxml parser context
    xmlParserCtxtPtr context;
    NSURLConnection *rssConnection;
    NSMutableArray *parsedIncidents;
    // Overall state of the parser, used to exit the run loop.
    BOOL done;
    // State variable used to determine whether or not to ignore a given XML element
    BOOL parsingAIncident;
    // The following state variables deal with getting character data from XML elements. This is a potentially expensive 
    // operation. The character data in a given element may be delivered over the course of multiple callbacks, so that
    // data must be appended to a buffer. The optimal way of doing this is to use a C string buffer that grows exponentially.
    // When all the characters have been delivered, an NSString is constructed and the buffer is reset.
    BOOL storingCharacters;
    NSMutableData *characterBuffer;
    // A reference to the current song the parser is working with.
    Incident *currentIncident;
    // The number of parsed songs is tracked so that the autorelease pool for the parsing thread can be periodically
    // emptied to keep the memory footprint under control. 
    NSUInteger countOfParsedIncidents;
    NSAutoreleasePool *downloadAndParsePool;
    NSDateFormatter *parseFormatter;

@property (nonatomic, assign) id <IncidentsImporterDelegate> delegate;
@property (nonatomic, retain) NSMutableArray *parsedIncidents;
@property BOOL storingCharacters;
@property (nonatomic, retain) NSMutableData *characterBuffer;
@property BOOL done;
@property BOOL parsingAIncident;
@property NSUInteger countOfParsedIncidents;
@property (nonatomic, retain) Incident *currentIncident;
@property (nonatomic, retain) NSURLConnection *rssConnection;
@property (nonatomic, retain) NSDateFormatter *parseFormatter;
// The autorelease pool property is assign because autorelease pools cannot be retained.
@property (nonatomic, assign) NSAutoreleasePool *downloadAndParsePool;

- (void)downloadAndParse:(NSURL *)url;
- (void)finishedCurrentIncident;

- (void)start;

- (void)downloadStarted;
- (void)downloadEnded;
- (void)parseEnded;
- (void)parsedIncident:(Incident *)incident;
- (void)parseError:(NSError *)error;
- (void)addToParseDuration:(NSNumber *)duration;

@end

还有 parser.m:

#import "IncidentsImporter.h"
#import "Incident.h"
#import <libxml/tree.h>

static NSUInteger kCountForNotification = 10;

// Function prototypes for SAX callbacks. This sample implements a minimal subset of SAX callbacks.
// Depending on your application's needs, you might want to implement more callbacks.
static void startElementSAX(void *ctx, const xmlChar *localname, const xmlChar *prefix, const xmlChar *URI, int nb_namespaces, const xmlChar **namespaces, int nb_attributes, int nb_defaulted, const xmlChar **attributes);
static void endElementSAX(void *ctx, const xmlChar *localname, const xmlChar *prefix, const xmlChar *URI);
static void charactersFoundSAX(void * ctx, const xmlChar * ch, int len);
static void errorEncounteredSAX(void * ctx, const char * msg, ...);

// Forward reference. The structure is defined in full at the end of the file.
static xmlSAXHandler simpleSAXHandlerStruct;

@implementation IncidentsImporter

@synthesize delegate, rssConnection, done, parsingAIncident, parsedIncidents, storingCharacters, currentIncident, countOfParsedIncidents, characterBuffer, parseFormatter, downloadAndParsePool;

- (void)start 
    NSLog(@"URL Gained");
    [[NSURLCache sharedURLCache] removeAllCachedResponses];
    NSURL *url = [NSURL URLWithString:@"http://ax.phobos.apple.com.edgesuite.net/WebObjects/MZStore.woa/wpa/MRSS/newreleases/limit=300/rss.xml"];
    [NSThread detachNewThreadSelector:@selector(downloadAndParse:) toTarget:self withObject:url];


- (void)downloadStarted 
    NSLog(@"Download has begun");
    NSAssert2([NSThread isMainThread], @"%s at line %d called on secondary thread", __FUNCTION__, __LINE__);
    [UIApplication sharedApplication].networkActivityIndicatorVisible = YES;


- (void)downloadEnded 
    NSLog(@"Download has ended");
    NSAssert2([NSThread isMainThread], @"%s at line %d called on secondary thread", __FUNCTION__, __LINE__);
    [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;


- (void)parseEnded 
    NSLog(@"Parsing has ended");
    NSAssert2([NSThread isMainThread], @"%s at line %d called on secondary thread", __FUNCTION__, __LINE__);
    if (self.delegate != nil && [self.delegate respondsToSelector:@selector(parser:didParseIncidents:)] && [parsedIncidents count] > 0) 
        [self.delegate parser:self didParseIncidents:parsedIncidents];
    
    [self.parsedIncidents removeAllObjects];
    if (self.delegate != nil && [self.delegate respondsToSelector:@selector(parserDidEndParsingData:)]) 
        [self.delegate parserDidEndParsingData:self];
    


- (void)parsedIncident:(Incident *)incident 
    NSLog(@"Parsing has begun on thread");
    NSAssert2([NSThread isMainThread], @"%s at line %d called on secondary thread", __FUNCTION__, __LINE__);
    [self.parsedIncidents addObject:incident];
    if (self.parsedIncidents.count > kCountForNotification) 
        if (self.delegate != nil && [self.delegate respondsToSelector:@selector(parser:didParseIncidents:)]) 
            [self.delegate parser:self didParseIncidents:parsedIncidents];
        
        [self.parsedIncidents removeAllObjects];
    


- (void)parseError:(NSError *)error 
    NSLog(@"Parsing has an error");
    NSAssert2([NSThread isMainThread], @"%s at line %d called on secondary thread", __FUNCTION__, __LINE__);
    if (self.delegate != nil && [self.delegate respondsToSelector:@selector(parser:didFailWithError:)]) 
        [self.delegate parser:self didFailWithError:error];
    


- (void)addToParseDuration:(NSNumber *)duration 
    NSAssert2([NSThread isMainThread], @"%s at line %d called on secondary thread", __FUNCTION__, __LINE__);


/*
 This method is called on a secondary thread by the superclass. We have asynchronous work to do here with downloading and parsing data, so we will need a run loop to prevent the thread from exiting before we are finished.
 */
- (void)downloadAndParse:(NSURL *)url 
    NSLog(@"Downloading and Parsing");
    self.downloadAndParsePool = [[NSAutoreleasePool alloc] init];
    done = NO;
    self.parseFormatter = [[[NSDateFormatter alloc] init] autorelease];
    [parseFormatter setDateStyle:NSDateFormatterLongStyle];
    [parseFormatter setTimeStyle:NSDateFormatterNoStyle];
    // necessary because iTunes RSS feed is not localized, so if the device region has been set to other than US
    // the date formatter must be set to US locale in order to parse the dates
    [parseFormatter setLocale:[[[NSLocale alloc] initWithLocaleIdentifier:@"AU"] autorelease]];
    self.characterBuffer = [NSMutableData data];
    [[NSURLCache sharedURLCache] removeAllCachedResponses];
    NSURLRequest *theRequest = [NSURLRequest requestWithURL:url];
    // create the connection with the request and start loading the data
    rssConnection = [[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
    // This creates a context for "push" parsing in which chunks of data that are not "well balanced" can be passed
    // to the context for streaming parsing. The handler structure defined above will be used for all the parsing. 
    // The second argument, self, will be passed as user data to each of the SAX handlers. The last three arguments
    // are left blank to avoid creating a tree in memory.
    context = xmlCreatePushParserCtxt(&simpleSAXHandlerStruct, self, NULL, 0, NULL);
    [self performSelectorOnMainThread:@selector(downloadStarted) withObject:nil waitUntilDone:NO];
    if (rssConnection != nil) 
        do 
            [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
         while (!done);
    
    // Release resources used only in this thread.
    xmlFreeParserCtxt(context);
    self.characterBuffer = nil;
    self.parseFormatter = nil;
    self.rssConnection = nil;
    self.currentIncident = nil;
    [downloadAndParsePool release];
    self.downloadAndParsePool = nil;


#pragma mark NSURLConnection Delegate methods

/*
 Disable caching so that each time we run this app we are starting with a clean slate. You may not want to do this in your application.
 */
- (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse 
    return nil;


// Forward errors to the delegate.
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error 
    done = YES;
    [self performSelectorOnMainThread:@selector(parseError:) withObject:error waitUntilDone:NO];


// Called when a chunk of data has been downloaded.
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data 
    NSLog(@"Did receive data");
    NSTimeInterval start = [NSDate timeIntervalSinceReferenceDate];
    // Process the downloaded chunk of data.
    xmlParseChunk(context, (const char *)[data bytes], [data length], 0);
    NSTimeInterval duration = [NSDate timeIntervalSinceReferenceDate] - start;
    [self performSelectorOnMainThread:@selector(addToParseDuration:) withObject:[NSNumber numberWithDouble:duration] waitUntilDone:NO];


- (void)connectionDidFinishLoading:(NSURLConnection *)connection 
    NSLog(@"Connection done loading");
    [self performSelectorOnMainThread:@selector(downloadEnded) withObject:nil waitUntilDone:NO];
    NSTimeInterval start = [NSDate timeIntervalSinceReferenceDate];
    // Signal the context that parsing is complete by passing "1" as the last parameter.
    xmlParseChunk(context, NULL, 0, 1);
    NSTimeInterval duration = [NSDate timeIntervalSinceReferenceDate] - start;
    [self performSelectorOnMainThread:@selector(addToParseDuration:) withObject:[NSNumber numberWithDouble:duration] waitUntilDone:NO];
    [self performSelectorOnMainThread:@selector(parseEnded) withObject:nil waitUntilDone:NO];
    // Set the condition which ends the run loop.
    done = YES; 


#pragma mark Parsing support methods

static const NSUInteger kAutoreleasePoolPurgeFrequency = 20;

- (void)finishedCurrentIncident 
    [self performSelectorOnMainThread:@selector(parsedIncident:) withObject:currentIncident waitUntilDone:NO];
    // performSelectorOnMainThread: will retain the object until the selector has been performed
    // setting the local reference to nil ensures that the local reference will be released
    self.currentIncident = nil;
    countOfParsedIncidents++;
    // 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 (countOfParsedIncidents == kAutoreleasePoolPurgeFrequency) 
        [downloadAndParsePool release];
        self.downloadAndParsePool = [[NSAutoreleasePool alloc] init];
        countOfParsedIncidents = 0;
    


/*
 Character data is appended to a buffer until the current element ends.
 */
- (void)appendCharacters:(const char *)charactersFound length:(NSInteger)length 
    [characterBuffer appendBytes:charactersFound length:length];


- (NSString *)currentString 
    // Create a string with the character data using UTF-8 encoding. UTF-8 is the default XML data encoding.
    NSString *currentString = [[[NSString alloc] initWithData:characterBuffer encoding:NSUTF8StringEncoding] autorelease];
    [characterBuffer setLength:0];
    return currentString;


@end

#pragma mark SAX Parsing Callbacks

// The following constants are the XML element names and their string lengths for parsing comparison.
// The lengths include the null terminator, to ensure exact matches.
static const char *kName_Item = "item";
static const NSUInteger kLength_Item = 5;
static const char *kName_Title = "title";
static const NSUInteger kLength_Title = 6;
static const char *kName_Category = "link";
static const NSUInteger kLength_Category = 9;
static const char *kName_Itms = "itms";
static const NSUInteger kLength_Itms = 5;
static const char *kName_Artist = "description";
static const NSUInteger kLength_Artist = 7;
static const char *kName_Album = "guid";
static const NSUInteger kLength_Album = 6;
static const char *kName_ReleaseDate = "pubDate";
static const NSUInteger kLength_ReleaseDate = 12;

/*
 This callback is invoked when the parser finds the beginning of a node in the XML. For this application,
 out parsing needs are relatively modest - we need only match the node name. An "item" node is a record of
 data about a song. In that case we create a new Song object. The other nodes of interest are several of the
 child nodes of the Song currently being parsed. For those nodes we want to accumulate the character data
 in a buffer. Some of the child nodes use a namespace prefix. 
 */
static void startElementSAX(void *ctx, const xmlChar *localname, const xmlChar *prefix, const xmlChar *URI, 
                            int nb_namespaces, const xmlChar **namespaces, int nb_attributes, int nb_defaulted, const xmlChar **attributes) 
    IncidentsImporter *parser = (IncidentsImporter *)ctx;
    // The second parameter to strncmp is the name of the element, which we known from the XML schema of the feed.
    // The third parameter to strncmp is the number of characters in the element name, plus 1 for the null terminator.
    if (prefix == NULL && !strncmp((const char *)localname, kName_Item, kLength_Item)) 
        NSLog(@"Found node");
        Incident *newIncident = [[Incident alloc] init];
        parser.currentIncident = newIncident;
        [newIncident release];
        parser.parsingAIncident = YES;
     else if (parser.parsingAIncident && ( (prefix == NULL && (!strncmp((const char *)localname, kName_Title, kLength_Title) || !strncmp((const char *)localname, kName_Category, kLength_Category))) || ((prefix != NULL && !strncmp((const char *)prefix, kName_Itms, kLength_Itms)) && (!strncmp((const char *)localname, kName_Artist, kLength_Artist) || !strncmp((const char *)localname, kName_Album, kLength_Album) || !strncmp((const char *)localname, kName_ReleaseDate, kLength_ReleaseDate))) )) 
        parser.storingCharacters = YES;
    


/*
 This callback is invoked when the parse reaches the end of a node. At that point we finish processing that node,
 if it is of interest to us. For "item" nodes, that means we have completed parsing a Song object. We pass the song
 to a method in the superclass which will eventually deliver it to the delegate. For the other nodes we
 care about, this means we have all the character data. The next step is to create an NSString using the buffer
 contents and store that with the current Song object.
 */
static void endElementSAX(void *ctx, const xmlChar *localname, const xmlChar *prefix, const xmlChar *URI)     
    IncidentsImporter *parser = (IncidentsImporter *)ctx;
    if (parser.parsingAIncident == NO) return;
    if (prefix == NULL) 
        if (!strncmp((const char *)localname, kName_Item, kLength_Item)) 
            [parser finishedCurrentIncident];
            parser.parsingAIncident = NO;
         else if (!strncmp((const char *)localname, kName_Title, kLength_Title)) 
            NSLog(@"Parsing title");
            parser.currentIncident.title = [parser currentString];
         else if (!strncmp((const char *)localname, kName_Category, kLength_Category)) 
            NSLog(@"Parsing url");
            parser.currentIncident.link = [NSURL URLWithString: [parser currentString]];
        
        else if (!strncmp((const char *)localname, kName_Artist, kLength_Artist)) 
            NSLog(@"Parsing description");
            parser.currentIncident.description = [parser currentString];
         else if (!strncmp((const char *)localname, kName_Album, kLength_Album)) 
            NSLog(@"Parsing guid");
            parser.currentIncident.guid = [parser currentString];
         else if (!strncmp((const char *)localname, kName_ReleaseDate, kLength_ReleaseDate)) 
            NSLog(@"Parsing date");
            NSString *dateString = [parser currentString];
            parser.currentIncident.pubDate = [parser.parseFormatter dateFromString:dateString];
        
    
    parser.storingCharacters = NO;


/*
 This callback is invo

【问题讨论】:

【参考方案1】:

注释掉该行后,您如何获得事件?

- (void)parser:(IncidentsImporter *)parser didParseIncidents:(NSArray *)parsedIncidents 
    //[incidents addObjectsFromArray: parsedIncidents];

在事件没有改变时调用 reloadData 并没有真正做任何事情。

在同一个函数中,只有在表静止时才调用 reloadData。如果您不调用它,则应设置一个标志以在表静止后调用 reloadData。有一些 UIScrollView 委托回调可以检测到这一点。

【讨论】:

OK 取消注释该行(不知道为什么我首先评论它)但仍然没有运气!但是我在这里设置了一个断点,它没有被调用。我怀疑不好,这可能是我的问题。【参考方案2】:

需要检查的一些事项:

如果在 parserDidEndParsingData: 中设置断点,ctableView 是否指向有效的 UITableView 对象,而不是 nil?

如果您在该行的 gdb 提示符下键入“po events”,您的事件是否全部加载?

如果键入“po [self tableView]”,是否显示与“po ctableView”相同的对象?

如果您在 tableView:numberOfRowsInSection: 中设置断点并执行“po 事件”,它们是否都加载在那里? (它会多次命中该断点,因此请继续点击并确保事件最终出现在那里。)

如果您的 IB 处于列表视图模式,您的 tableView 应该看起来嵌套在 CurrentIncidentsTableViewController 下。这表明 tableView 连接到 UITableViewController 的 view 属性。 (顺便说一句,如果你有这个配置的属性,你可以使用 self.tableView 从你的控制器访问 tableView,所以从技术上讲你不需要 ctableView。)

tableView 的 dataSource 和 delegate 属性应该连接到 IB 中的 CurrentIncidentsTableViewController。

【讨论】:

好的,已经添加了结果。不好,我不认为。还添加了解析器代码以防万一。 好的,所以事件为零。它应该在 beginParsing 中创建。在那里设置断点。它曾经被调用吗?此外,您还有 [incidents addObjectsFromArray: parsedIncidents];在 parser:didParseIncidents: 中注释掉。一旦确定数组实际上已被创建,您可能需要取消注释。 OK 取消注释(不知道为什么要注释,但无论如何)并在 beginParsing 中设置断点。有趣的是,当它到达 IF 语句时,每次它都采用 else 路径,而不是 if 路径,而我怀疑它应该采用 if 路径。任何想法为什么它不会这样做?我签入了示例应用程序,它确实采用了 if 选项而不是 else。 我还在 didParseIncidents 调用中设置了一个断点,但根本没有调用它。难道这就是问题所在?【参考方案3】:

解析完成后尝试重新加载表。

[table reloadData];

table是interface builder创建的table view的实例。

link 提供了一个示例程序,用于解析 xml 并在表格中显示其内容。看看这个。希望这也对你有帮助。

一切顺利。

【讨论】:

下载 tabrss 示例项目并将链接替换为您的链接。使用此代码解析 xml。 是的,这是一个很好的例子,但我已经有一个应用程序可以做到这一点 - 我正在升级后端,因为我想使用辅助线程来解析以阻止主线程在数据时冻结正在下载。

以上是关于UITableView 不显示解析的数据的主要内容,如果未能解决你的问题,请参考以下文章

UITableview如何一次只显示一个单元格

为啥我的 UITableView 不显示数据?

JSON 解析:在 UITableView 上显示数据

UITableView 和单元格重用

UITableView 中的第二个视图不会显示来自 JSON 的解析数据

UIViewController中的UITableView不显示json?