NSMutableDictionary不会添加到NSMutableArray中

时间:2009-10-15 16:27:58

标签: iphone objective-c

我无法使用此代码填充previewData NSMutableArray。 XML请求已成功检索,解析但我无法填充tableviewController的previewData数组。

PreviewsController.h代码:

#import <UIKit/UIKit.h>

@interface PreviewsController : UIViewController 
    <UITableViewDelegate, UITableViewDataSource>
{
    UITableView *previewList;
    NSString *enteredUsername;
    NSString *enteredPassword;
    NSMutableString *current_xml_element;
    NSMutableString *current_xml_value;
    NSMutableDictionary *item;
    NSMutableArray *previewData;
}

@property (nonatomic, retain) IBOutlet UITableView *previewList;
@property (nonatomic, retain) NSString *enteredUsername;
@property (nonatomic, retain) NSString *enteredPassword;
@property (nonatomic, retain) NSMutableString *current_xml_element;
@property (nonatomic, retain) NSMutableString *current_xml_value;
@property (nonatomic, retain) NSMutableArray *previewData;
@property (nonatomic, retain) NSMutableDictionary *item;

@end

PreviewsController.m代码:

#import "PreviewsController.h"

@implementation PreviewsController

@synthesize previewList;
@synthesize enteredUsername, enteredPassword;
@synthesize current_xml_element, previewData, item, current_xml_value;

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

    // Release any cached data, images, etc that aren't in use.
}

- (void)viewDidUnload {
    // Release any retained subviews of the main view.
    // e.g. self.myOutlet = nil;
    self.previewData = nil;

    [super viewDidUnload];
}

- (void)dealloc {
    [item release];
    [previewData release];
    [enteredUsername release];
    [enteredPassword release];
    [current_xml_element release];
    [super dealloc];
}

- (void)viewDidLoad {
    previewData = [[NSMutableArray alloc] init];

    // Fetch the XML output from API - Start
    NSString *APIURL = [[NSString alloc] initWithFormat:@"http://mysite.com/api/%@/%@", enteredUsername, enteredPassword];
    NSURL *url = [[NSURL alloc] initWithString:APIURL];
    NSXMLParser *xmlParser = [[NSXMLParser alloc] initWithContentsOfURL:url];
    [APIURL release];
    [url release];

    [xmlParser setDelegate:self];
    [xmlParser parse];
    // Fetch the XML output from API - End

    [super viewDidLoad];
}

#pragma mark -
#pragma mark XML Request Methods

-(void)parserDidStartDocument:(NSXMLParser *)parser {
}

-(void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict {
    current_xml_element = [elementName copy];

    if ([elementName isEqualToString:@"subject"]) {
        item = [[[NSMutableDictionary alloc] init] autorelease];
        current_xml_value = [[NSMutableString alloc] init];
    }
}

-(void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
    if ([current_xml_element isEqualToString:@"subject"]) {
        [current_xml_value appendString:string];
    }
}

-(void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
    if ([elementName isEqualToString:@"subject"]) {
        [item setObject:current_xml_value forKey:@"subject"];
        [previewData addObject:[item copy]];
//      NSLog(@"array count: %@", [previewData count]);
    }
}

-(void)parserDidEndDocument:(NSXMLParser *)parser {
}

-(void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError {
    NSLog(@"Error occurred: %d - %@", [parseError code], [parseError localizedDescription]);
}

#pragma mark -
#pragma mark Table View Data Source Methods

-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 
    return [previewData count];
}

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *SimpleTableIdentifier = @"SimpleTableIdentifier";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:SimpleTableIdentifier];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:SimpleTableIdentifier] autorelease];

    }
    NSUInteger row = [indexPath row];
    cell.textLabel.text = [previewData objectAtIndex:row];
    return cell;
}


@end

当我构建并运行此代码时,在控制台中返回以下错误:

2009-10-15 23:13:55.296 PreviewMyEmail[29964:207] *** -[NSCFDictionary isEqualToString:]: unrecognized selector sent to instance 0x3839a60
2009-10-15 23:13:55.298 PreviewMyEmail[29964:207] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[NSCFDictionary isEqualToString:]: unrecognized selector sent to instance 0x3839a60'
2009-10-15 23:13:55.298 PreviewMyEmail[29964:207] Stack: (
    29307995,
    2531364681,
    29689915,
    29259382,
    29112002,
    3812419,
    13553,
    3063392,
    3029655,
    3106211,
    3070291,
    55820976,
    55820399,
    55818438,
    55817530,
    55851064,
    29094482,
    29091423,
    29088840,
    37398413,
    37398610,
    2781187,
    8436,
    8290
)

我在哪里做错了?谢谢你的帮助。

干杯。

3 个答案:

答案 0 :(得分:7)

是否在其他地方初始化了previewData?在给定代码和输出的情况下,它看起来像它没有值。 [previewData count]将返回nil,即0。在开头添加previewData = [[NSMutableArray alloc] init];,它应该有效。

答案 1 :(得分:5)

可能会在-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath中生成错误,特别是行:

cell.textLabel.text = [previewData objectAtIndex:row];

在这里,您要将NSMutableDictionary分配给类型为NSString的属性。应用程序的开发版本应该显示堆栈跟踪中的方法名称和行号。

与错误无关的注释

效率

由于NS集合类保留了对象,因此除非需要不可变对象,否则不需要存储item的副本而不是原始副本。

内存泄漏

如果存在多个“subject”元素,则一些复制的对象将产生内存泄漏。您可以自己处理版本或使用属性。属性更容易处理,因此更不容易出错。如果您不打算使用它们,为什么还要定义属性呢?某些属性不是公共接口的一部分,并且该类看起来不适用于库。如果您希望自己释放句柄,请记住在一个方法中使用自动/释放来平衡alloc / copy / retain。对于解析器:didStartElement:...,相应的方法是解析器:didEndElement:...我仍在尝试制定一种描述这种平衡的简洁方法(即将推出an SO question near you)。

每次解析器进入子进程时,

current_xml_element都会被破坏。相反,您可以在开始/结束处理“主题”节点时设置/取消设置标志。如果“主题”节点曾经包含子“主题”节点,这将会中断,但是这样的XML文档将是病态的并导致其他问题(例如,破坏itemcurrent_xml_value,从而导致内存泄漏) 。标志的替代方法是存储一堆元素,在didStartElement期间推送并在didEndElement期间弹出。

PreviewsController.h:

@interface PreviewsController : UIViewController 
    <UITableViewDelegate, UITableViewDataSource>
{
    ...
    /* NSMutableString *current_xml_element; */
    BOOL current_element_is_subject;
    ...
}

...
/* @property (nonatomic, retain) NSMutableString *current_xml_element; */
@property (nonatomic, assign) BOOL current_element_is_subject;
...

@end

PreviewsController.m

-(void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict {    
    if ([elementName isEqualToString:@"subject"]) {
        self.item = [NSMutableDictionary dictionaryWithCapacity:1];
        self.current_xml_value = [NSMutableString stringWithCapacity:80];
        current_element_is_subject = YES;
    }
}
-(void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
    if (current_element_is_subject) {
        [current_xml_value appendString:string];
    }
}

-(void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
    if ([elementName isEqualToString:@"subject"]) {
        [item setObject:current_xml_value forKey:@"subject"];
        [previewData addObject:item];
        current_element_is_subject = NO;
//      NSLog(@"array count: %@", [previewData count]);
    }
}

博客条目“Make NSXMLParser your friend”似乎与您想要的基本相同。研究它可能会有所帮助。

答案 2 :(得分:1)

确保初始化previewData - 如果您从未初始化数组,那么它将是nil值。在Objective-C中,您可以向nil发送消息,因此发送addObject:消息将不会执行任何操作,[nil count]将返回0.