NSMutableDictionary is not added to NSMutableArray

I cannot populate the previewData NSMutableArray file with this code. The XML request is successfully retrieved, parsed, but I cannot populate the previewData array for the tableviewController.

PreviewsController.h code:

#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 code:

#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

      

When I build and run this code, the following error is returned in the console:

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
)

      

Where am I doing wrong? Thank you for your help.

Greetings.

+2


source to share


3 answers


The error can be generated in -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

, in particular, in the line:

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

      

Here you are assigning an NSMutableDictionary to a property of type NSString. The development version of your application should show you the method name and line number in the stack trace.

Non-bug related notes

Efficiency

Since the NS collection classes persist objects, there is no need to keep a copy item

and not the original unless you want an immutable object.

Memory leaks



Some of the copied objects will create memory leaks if there is more than one subject element. You can handle the issue yourself or use properties. Properties are easier to handle, and therefore less error prone. Why define properties if you're not going to use them? Some of the properties are not part of the public interface and the class does not look like a library. If you want the descriptor to be deallocated, remember to balance the alloc / copy / retain function in one way with automatic release in the appropriate method. For the parser: didStartElement: ..., the corresponding method is the parser: didEndElement: ... I am still trying to formulate a concise way to describe this balance (there will be a SO question near you soon).

current_xml_element

gets clobbered every time the parser descends into a child. Instead, you can set / uncheck the flag when starting / finishing processing the "theme" node. This will break if the "subject" node ever contains a child "object" node, but such an XML document would be pathological and cause other problems (like clobbering item

and current_xml_value

leading to memory leaks). An alternative flag is to keep a stack of elements by pushing into didStartElement time and popping out during 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]);
    }
}

      

The blog post " Make NSXMLParser your friend " seems to do basically what you want. It would probably be useful to study it.

+5


source


Is the preview initialized somewhere else? It doesn't seem to matter to me, given the code and output. [previewData count]

returns nil, which is 0. Add previewData = [[NSMutableArray alloc] init];

at the beginning and it should work.



+7


source


Make sure you initialize previewData

- if you never initialize the array in the first place, this will be the value nil

. In Objective-C, you can send a message to nil

, so sending a message will addObject:

do nothing but [nil count]

return 0.

+1


source







All Articles