iPhone: memory leak on autoreleased object? - iphone

I am using the XMLParser class, which contains an array with XMLElement objects. The XMLElement is being allocated using the autorelease operation. However, for some reason I'm getting a memory leak (using instruments) on this line:
self.currentElement = [[[XMLElement alloc] init] autorelease];
I'm also releasing the XMLParser object in the caller class. I've highlighted the "problematic" lines in the XMLParser source code below as comment.
I really tried everything to fix it, but unfortunately I do not understand why this is happening at all. Any help is appreciated!
Thank you very much!
// --- XMLElement
#import <Foundation/Foundation.h>
#interface XMLElement : NSObject {
NSDictionary *attributes;
NSMutableArray *children;
NSString *textValue;
NSString *tagName;
XMLElement *parentElement;
}
-(id) init;
-(void) addChild:(XMLElement*) child;
#property(nonatomic, retain) NSDictionary *attributes;
#property(nonatomic, retain) NSMutableArray *children;
#property(nonatomic, retain) NSString *textValue;
#property(nonatomic, retain) NSString *tagName;
#property(nonatomic, retain) XMLElement *parentElement;
#end
#import "XMLElement.h"
#implementation XMLElement
#synthesize attributes, children, textValue, parentElement, tagName;
-(id)init {
if(self = [super init]) {
children = [[NSMutableArray alloc] init];
}
return self;
}
-(void) addChild:(XMLElement*) child{
[self.children addObject:child];
}
- (void)dealloc {
[attributes release];
[children release];
[textValue release];
[tagName release];
[parentElement release];
[super dealloc];
}
#end
// --- XMLParser
#import <Foundation/Foundation.h>
#import "XMLElement.h"
#interface XMLParser : NSObject<NSXMLParserDelegate> {
XMLElement *currentElement;
XMLElement *currentParentElement;
NSMutableString *currentElementValue;
}
- (BOOL)parseData: (NSData*) dataToParse;
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName
attributes:(NSDictionary *)attributeDict;
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string;
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName;
#property (nonatomic, retain) XMLElement *currentParentElement;
#property (nonatomic, retain) XMLElement *currentElement;
#property (nonatomic, retain) NSMutableString *currentElementValue;
#end
#import "XMLParser.h"
#implementation XMLParser
#synthesize currentElementValue, currentElement, currentParentElement;
- (BOOL)parseData: (NSData*) dataToParse {
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:dataToParse];
[parser setDelegate:self];
BOOL success = [parser parse];
[parser release];
return success;
}
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName
attributes:(NSDictionary *)attributeDict{
if(currentElement){
self.currentParentElement = currentElement;
}
// ------------------------------------------------------------
// Instruments is marking this line as source of the leak with 90%
self.currentElement = [[[XMLElement alloc] init] autorelease];
// --------
currentElement.tagName = elementName;
currentElement.attributes = attributeDict;
currentElement.parentElement = self.currentParentElement;
if(self.currentParentElement){
[self.currentParentElement addChild:currentElement]; // and this one with 10%
}
self.currentElementValue = nil;
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{
if(!currentElement) {
return;
}
if(currentElementValue == nil)
self.currentElementValue = [NSMutableString stringWithString:string];
else
[currentElementValue appendString:string];
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName{
if(currentElement == nil){
if( currentParentElement.parentElement){
self.currentParentElement = currentParentElement.parentElement;
}
}else{
currentElement.textValue = currentElementValue;
[currentElementValue release];
currentElementValue = nil;
self.currentParentElement = currentElement.parentElement;
currentElement = nil;
}
}
- (void)dealloc {
[currentParentElement release];
[currentElement release];
[super dealloc];
}
#end

You must release currentElement in your dealloc method in XMLParser.
It's created as autorelease but then assigned to a retain property, so you are actually retaining it once (which is good).
UPDATE: you should do self.currentElement = nil; when you are done with it, not currentElement = nil;.

This:
self.currentElement = [[[XMLElement alloc] init] autorelease];
retains currentElement (because the property is declared with retain).
Later, in parser:didEndElement:namespaceURI:qualifiedName:, you do this:
currentElement = nil;
which causes the leak because you are not releasing currentElement.

From what I see on this page, this is a documented apple bug. I have experienced the same problem in some of my apps...

Is your XMLParser being run on a background thread? If so, you need to create an NSAutoReleasePool for that thread in order for your [[[XMLElement alloc] init] autorelease] call to work.

HI guys.
the reason is just one simple thing,
instead of
#property(nonatomic, retain) XMLElement *parentElement;
must be
#property(nonatomic, assign) XMLElement *parentElement;
also you need delete [parentElement release] from dealloc
reason is that you are creating cyclic referances. parent to child and chils to parent
and when you are realesing parser object the elements still ocurrs in memory with pointer xount > 0

Related

Application start debuging at pool drain instruction

I am build an iPhone application who try to perform a login via a Rest web service (WCF).
The part who call the service work correctly. I receive data from the called service without any problem.
But I have to parse the message using the NSXMLParser. When I introduce it in my application (this is the first time I used it) the application parse the message correctly but at the end of the application execution it's lunch gdb and pause the application at the start of [pool drain] instruction.
#interface MessageParser : NSObject <NSXMLParserDelegate> {
NSMutableString* currentProperty;
Message* message;
}
#property (nonatomic, retain) NSMutableString* currentProperty;
#property (nonatomic, retain) Message* message;
- (void)parseMessageData:(NSData *)data parseError:(NSError **)err;
#end
#implementation MessageParser
#synthesize message, currentProperty;
- (void)parseMessageData:(NSData *)data parseError:(NSError **)err {
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data];
[parser setDelegate:self];
[parser setShouldProcessNamespaces:NO];
[parser setShouldReportNamespacePrefixes:NO];
[parser setShouldResolveExternalEntities:NO];
[parser parse];
if (err && [parser parserError]) {
*err = [parser parserError];
}
[parser release];
}
-(void) parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict{
if(qName){
elementName = qName;
}
if([elementName isEqualToString:#"Message"]){
self.message = [[Message alloc] init];
}
else if( [elementName isEqualToString:#"body"] || [elementName isEqualToString:#"code"] || [elementName isEqualToString:#"error"]){
currentProperty = [NSMutableString string];
}
}
-(void) parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName{
if(qName){
elementName = qName;
}
if(message){
if([elementName isEqualToString:#"body"]){
self.message.messageBody = currentProperty;
}
else if([elementName isEqualToString:#"error"]){
self.message.error = currentProperty;
}
else if([elementName isEqualToString:#"code"]){
NSNumberFormatter * f = [[NSNumberFormatter alloc] init];
[f setNumberStyle:NSNumberFormatterDecimalStyle];
self.message.messageCode = [f numberFromString:currentProperty];
[f release];
}
}
self.currentProperty = nil;
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)foundedCharacters
{
if (self.currentProperty) {
[currentProperty appendFormat:#"%#", foundedCharacters];
}
}
Some time the debugger give me this message : Attempting to create USE_BLOCK_IN_FRAME variable with block that isn't in the frame.
Do you have any idea ?
Thank you.
This can be done using method swizzling:
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#interface NSAutoreleasePool(customDrain)
-(void) myDrain;
#end
#implementation NSAutoreleasePool(customDrain)
-(void) myDrain
{
if ([self respondsToSelector:#selector(myDrain)])
{
NSLog(#"Draining...");
// note that #selector(myDrain) was remapped
// to #selector(drain) during the process of
// the swizzling, so now, myDrain calls drain,
// and drain calls myDrain.
[self myDrain];
}
else
{
NSLog(#"Remapping Failed!");
}
}
#end
void SwizzleMethod(Class c, SEL orig, SEL new);
void SwizzleMethod(Class c, SEL orig, SEL new) {
Method origMethod = class_getInstanceMethod(c, orig);
Method newMethod = class_getInstanceMethod(c, new);
if (origMethod && newMethod)
method_exchangeImplementations(origMethod, newMethod);
}
int main (int argc, const char * argv[])
{
NSAutoreleasePool *pool = [NSAutoreleasePool new];
SwizzleMethod([NSAutoreleasePool class], #selector(drain), #selector(myDrain));
[pool drain];
return 0;
}
You simply put your own code in the category's myDrain method, and, then, you run your own code when the pool is drained.
I hope this helped.

XMLParser Advice

I'm not sure what I am doing wrong. I have a URL leading to an XML tree that looks like:
<result>
...
<title>
...
</title>
<body>
...
</body>
...
</result>
I just need to parse the file and get the title and body. Here is my object.h:
#import <Foundation/Foundation.h>
#interface Object : NSObject
{
NSString *title;
NSString *description;
}
#property (nonatomic, retain) NSString *title;
#property (nonatomic, retain) NSString *description;
#end
And here is Object.m:
#import "Object.h"
#implementation Object
#synthesize title, description;
-(void) dealloc
{
[title release];
[description release];
[super dealloc];
}
#end
Here is my XMLParser.h:
#import <Foundation/Foundation.h>
#import "Object.h"
#interface XMLParser : NSObject <NSXMLParserDelegate>
{
NSMutableString *currentNodeContent;
NSMutableArray *arrayOfObjects;
NSXMLParser *parser;
Object *currentObject;
}
#property (readonly, retain) NSMutableArray *arrayOfObjects;
-(id) loadXMLbyURL:(NSString *)urlString;
#end
And finally, XMLParser.m:
#import "XMLParser.h"
#import "Object.h"
#implementation XMLParser
#synthesize arrayOfObjects;
-(id) loadXMLbyURL:(NSString *)urlString
{
arrayOfObjects = [[NSMutableArray alloc] init];
NSURL *url = [NSURL URLWithString:urlString];
NSData *data = [[[NSData alloc] initWithContentsOfURL:url] autorelease];
parser = [[NSXMLParser alloc] initWithData:data];
parser.delegate = self;
[parser parse];
return self;
}
-(void) dealloc
{
[parser release];
[super dealloc];
}
-(void) parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
if([elementName isEqualToString:#"result"])
{
currentObject = [Object alloc];
}
}
-(void) parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
if([elementName isEqualToString:#"title"])
{
currentObject.title = currentNodeContent;
}
if([elementName isEqualToString:#"body"])
{
currentObject.description = currentNodeContent;
}
if([elementName isEqualToString:#"result"])
{
[arrayOfObjects addObject:currentObject];
[currentObject release];
currentObject = nil;
[currentNodeContent release];
currentNodeContent = nil;
}
}
-(void) parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
currentNodeContent = (NSMutableString *) [string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
}
#end
In my main code I make a call to XML parser like this:
xmlParser = [[XMLParser alloc] loadXMLbyURL:#"www.websiteWithXML.com"];
However nothing is being placed into my arrayOfObjects (I know this because when I tell a tableview to have as many rows as my array, there are no rows).
please help!!! Thank you in advance!
Not sure if this will help...
Try changing:
arrayOfObjects = [[NSMutableArray alloc] init];
To:
arrayOfObjects = [[NSMutableArray alloc] initWithCapacity:0];
Other than that it appears that your code is fine. Also just because your table isn't loading anything doesn't mean that you do not have objects in your array. Use breakpoints to take a look at your data after you finish parsing and before you try to load your tableview.
Try resetting currentNodeContent inside each of your element starts. For example:
-(void) parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
if([elementName isEqualToString:#"result"])
{
currentObject = [Object alloc];
}
else if ([elementName isEqualToString:#"title"] || [elementName isEqualToString:#"body"])
{
[currentNodeContent setString:#""];
}
}
Then, when you receive characters do an append:
-(void) parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
[currentNodeContent appendString:[string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]];
}
By doing this, you handle the situation where a foundCharacters gets multiple times to capture all of the characters within a given element.

NSXMLParser multiple call - BAD ACCESS

hello i want to parse html an with this information another html file...
after 1-5 call the program crashes...
header:
#import <UIKit/UIKit.h>
#interface FirstViewController : UIViewController <ZBarReaderDelegate, NSXMLParserDelegate>{
UIImageView *resultImage;
UITextView *resultText;
NSString *product_link;
NSXMLParser *parseHTML;
NSXMLParser *parseHTML2;
NSMutableArray *myMutableArray;
id <NSXMLParserDelegate> testkollege, asdf;
}
#property (nonatomic, retain) IBOutlet UIImageView *resultImage;
#property (nonatomic, retain) IBOutlet UITextView *resultText;
#property (nonatomic, assign) IBOutlet NSString *product_link;
#property (nonatomic, assign) NSXMLParser *parseHTML;
#property (nonatomic, assign) NSXMLParser *parseHTML2;
#property (nonatomic, retain) NSMutableArray *myMutableArray;
#property (nonatomic, assign) id <NSXMLParserDelegate> testkollege;
#property (nonatomic, assign) id <NSXMLParserDelegate> asdf;
- (IBAction) scanButtonTapped;
#end
m-file:
#import "FirstViewController.h"
#import "/System/Library/Frameworks/Foundation.framework/Headers/NSDebug.h"
#implementation FirstViewController
#synthesize resultImage, resultText;
#synthesize product_link;
#synthesize parseHTML, parseHTML2;
#synthesize myMutableArray;
#synthesize testkollege, asdf;
bool link_is_here = false;
bool allergy_is_here = false;
bool parse_one_ok = true;
- (void) imagePickerController: (UIImagePickerController*) reader
didFinishPickingMediaWithInfo: (NSDictionary*) info
{
// ADD: get the decode results
id<NSFastEnumeration> results = [info objectForKey: ZBarReaderControllerResults];
ZBarSymbol *symbol = nil;
for(symbol in results)
// EXAMPLE: just grab the first barcode
break;
// EXAMPLE: do something useful with the barcode data
resultText.text = symbol.data;
// EXAMPLE: do something useful with the barcode image
resultImage.image =
[info objectForKey: UIImagePickerControllerOriginalImage];
// ADD: dismiss the controller (NB dismiss from the *reader*!)
[reader dismissModalViewControllerAnimated: YES];
parseHTML = [[NSXMLParser alloc] initWithContentsOfURL:[NSURL URLWithString:[#"http://url.com/suche/?q=" stringByAppendingString:symbol.data]] ];
NSLog(#"parser 1 start");
[parseHTML setDelegate:self];
[parseHTML parse];
NSLog(#"parser 1 ready");
[parseHTML release];
}
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName attributes:(NSDictionary *)attributeDict
{
for(NSString *key in [attributeDict allKeys]) {
if ([[attributeDict valueForKey:key] isEqualToString:#"search-result"]) {
link_is_here = true;
}
if ([key isEqualToString:#"href"] && link_is_here) {
product_link = [attributeDict valueForKey:key];
[parser abortParsing];
parseHTML2 = [[NSXMLParser alloc] initWithContentsOfURL:[NSURL URLWithString:[#"http://url.com" stringByAppendingString:product_link]]];
[parseHTML2 setDelegate:self];
parse_one_ok = true;
link_is_here = false;
[parseHTML2 parse];
}
if ([key isEqualToString:#"id"] && [[attributeDict valueForKey:key] isEqualToString:#"nutrition-allergy"]) {
allergy_is_here = true;
}
if ([key isEqualToString:#"title"] && allergy_is_here) {
NSLog(#"keys: %#",[attributeDict valueForKey:key]);
}
if ([key isEqualToString:#"id"] && [[attributeDict valueForKey:key] isEqualToString:#"another string"]) {
allergy_is_here = false;
parse_one_ok = true;
NSLog(#"Parser off");
[parser abortParsing];
}
}
}
-(void) parserDidEndDocument:(NSXMLParser *)parser{
if (parse_one_ok) {
[parseHTML2 release];
parse_one_ok = false;
}
}
-(void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{
}
-(void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName{
}
- (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;
}
- (void)dealloc {
[parseHTML release];
[parseHTML2 release];
self.product_link = nil;
self.resultImage = nil;
self.resultText = nil;
[super dealloc];
}
#end
that is simple. You are releasing ParseHTML NSXMLPArsetwice.
-(void) imagePickerController: (UIImagePickerController*) reader
didFinishPickingMediaWithInfo: (NSDictionary*) info
in the lastline
-(void)dealloc.
A object should be release only if you have earned the ownership. by retain copy etc. But you have allocated it only once so should release only once. But you did two releases .
You are also releasing NSXMLParser object parseHTML2 thrice. As per your code at any stage parseHTML2 will be released at least twice which is retained only once. ParseHTML1 objects case have been mentioned above
Regards,
Jackson Sunny Rodrigues
Turn on NSZombieEnabled. You are obviously releasing something you shouldn't be. When you do this, it will show you exactly where the bad access is occurring and you can trace back to where you are releasing the object. Check out this tutorial:
http://www.codza.com/how-to-debug-exc_bad_access-on-iphone
Best to learn how to fix it and what's wrong :)

iPhone NSMutableArray release problem

I have two controllers, SyncController and XMLController. SyncController sends some parameters to XMLController, which connects to an API and wraps the result as objects within an NSMutableArray, and returns the array back to the SyncController.
Some code:
SyncController.h
-(void)urlHandler:(NSArray *)urlHandler listObjectsFinishedLoading:(NSMutableArray *)resultData;
SyncController.m
- (void)urlHandler:(NSArray *)urlHandler listObjectsFinishedLoading:(NSMutableArray *)resultData;
{
NSMutableArray *receivedObjects = [[NSMutableArray alloc] init];
[receivedObjects addObjectsFromArray:resultData];
for (Object *o in receivedObjects) {
//DO SOME STUFF
}
[receivedObjects release];
}
XMLController.h
#interface XMLController : NSObject {
NSMutableArray *objects;
}
#property (nonatomic, retain) NSMutableArray *objects;
XMLController.m
-(void) connectionDidFinishLoading:(NSURLConnection *) connection {
objects = [[NSMutableArray alloc] init];
if ([delegate respondsToSelector:#selector(urlHandler:listObjectsFinishedLoading:)]) {
[delegate urlHandler:self listObjectsFinishedLoading:objects];
}
//[objects release];
}
-(void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
// Initialize an Object
}
-(void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName
{
// Put some properties unto Object
// Ad Object to the objects array
// release Object
}
- (void)parserDidEndDocument:(NSXMLParser *)parser
{
//[objects release];
}
- (void)dealloc {
//[objects release];
[super dealloc];
}
My question is, how do I properly release the objects array? If I don't release it, the code works properly (the actions from //DO SOME STUFF are executed) but it obviously leaks. If I release it, wherever I do it (see the commented //[objects release]; in three places) the app crashes.
Any suggestions? Thanks.
Try to
if ([delegate respondsToSelector:#selector(urlHandler:listObjectsFinishedLoading:)]) {
[delegate urlHandler:self listObjectsFinishedLoading:[objects autorelease]];
}
Maybe you're deallocating the Object at - (void)parserDidEndDocument:(NSXMLParser *)parser and again in - (void)dealloc. Try to set the object to nil (≠ NULL) when you release it, so you know it won't get released again.
- (void)parserDidEndDocument:(NSXMLParser *)parser
{
[objects release], objects = nil;
}
-(void) connectionDidFinishLoading:(NSURLConnection *) connection {
NSMutableArray *temp=[[NSMutableArray alloc] init];
self.objects=temp;
[temp release];
if ([delegate respondsToSelector:#selector(urlHandler:listObjectsFinishedLoading:)]) {
[delegate urlHandler:self listObjectsFinishedLoading:objects];
}
//[objects release];
}
and then
- (void)dealloc {
//[objects release];
[self.objects release];
[super dealloc];
}
use in this way it definitely works.
You are defining objects as a retained property, and then addressing the instance variable directly. If you are using synthesize to generate the getters and setters, then let them do the memory management for you.
self.objects = [[[NSMutableArray alloc] init] autorelease];
and
self.objects = nil;
rather than manually doing the releases.
thanks for your help. Still doesn't work, but I get the feeling that teh problem might be in the Object class. Releasing the array calls release on every object right?
Object.h
#interface Object : NSObject {
NSString *name;
}
#property (nonatomic, retain) NSString *name;
Object.m
-(void) dealloc{
[self.name release];
[super dealloc];
}
If I comment the [self.name release]; line, then the array in question CAN be released, it doesn't crash, and with no leaks. But then the app leaks NSStrings in other places where the Object name property is used.
It might be something very trivial that I miss.
Thanks.

iPhone XML parser wont add object to mutable array

Greetings,
I have a problem with adding an object from parser to mutable array.
It was working fine until I moved MtableArray from AppDelegate to ViewController. This is where I call parser (MutableArray is inside this View also):
NSURL *url = [[NSURL alloc] initWithString:#"http://example.com"];
NSXMLParser *xmlParser = [[NSXMLParser alloc] initWithContentsOfURL:url];
parser = [[XMLParser alloc] init];
[xmlParser setDelegate:parser];
[xmlParser parse];
...and this is inside parser where objects should be added:
if([elementName isEqualToString:#"Element"]) {
[viewController.marray addObject:parsedObj];
[parsedObj release];
parsedObj = nil;
}
marray is synthesized inside viewController. Parser is doing good job, I tried with NSLog, but marray.count is always (null). Please help!!!
Try like this,
[viewController.marray addObject:[parsedObj copy]];
Have you verified that the marray property is non-nil? If it somehow hasn't been set properly then all of the insertions will be no-ops, and the the result of the count method will be nil.
Now that you've posted more code, this line is your problem:
[marray init];
You need to alloc/init a new NSMutableArray; this line is simply sending the init message to nil.
Can you post more code?
If you are inside view controller when marray is being called, you shouldn't have to call viewController.marray, just marray
One suggestion, from my experience in parsing, is to use an NSMutableDictionary instead of an array..so, for instance:
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
currentElement = [elementName copy];
if([elementName isEqualToString:#"item"])
{
//Clear out story item caches
item = [[NSMutableDictionary alloc] init];
currentTitle = [[NSMutableString alloc] init];
currentAddress = [[NSMutableString alloc] init];
currentCity = [[NSMutableString alloc] init];
currentState = [[NSMutableString alloc] init];
currentZip = [[NSMutableString alloc] init];
currentId = [[NSMutableString alloc] init];
}
}
and then to add everything:
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
if([elementName isEqualToString:#"item"])
{
//Save values to an item, then store that item into the array
[item setObject:currentTitle forKey:#"title"];
[item setObject:currentAddress forKey:#"address"];
[item setObject:currentCity forKey:#"city"];
[item setObject:currentState forKey:#"state"];
[item setObject:currentZip forKey:#"zip"];
[item setObject:currentId forKey:#"id"];
// venues is the mutable array
[venues addObject:[item copy]];
} else {
return;
}
}
Now my mutable array has all the elements I need, and I can do various things with it (like reload a table cell). The above code has been tested and verified as working.
Here is some more code:
ViewController.h
#import <UIKit/UIKit.h>
#class XMLParser;
#interface ViewController : UIViewController{
NSMutableArray *marray;
XMLParser *parser;
}
#property (nonatomic, retain) NSMutableArray *marray;
#property (nonatomic, retain) XMLParser *parser;
#end
ViewController.m
#
import "ViewController.h"
#import "ParsedObj.h"
#import "XMLParser.h"
#synthesize marray;
#synthesize parser;
(...)
-(void)search:(id)sender{
[marray init];
NSURL *url = [[NSURL alloc] initWithString:#"http://example.com"];
NSXMLParser *xmlParser = [[NSXMLParser alloc] initWithContentsOfURL:url];
parser = [[XMLParser alloc] init];
[xmlParser setDelegate:parser];
[xmlParser parse];
(...)
#end
XMLparser.h:
#import <UIKit/UIKit.h>
#class ViewController, ParsedObj;
#interface XMLParser : NSObject <NSXMLParserDelegate>{
NSMutableString *currentElementValue;
ViewController *viewController;
ParsedObj *parsedObj;
}
#end
..and XMLparser.m:
#import "XMLParser.h"
#import "ViewController.h"
#import "ParsedObj.h"
#implementation XMLParser
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName
attributes:(NSDictionary *)attributeDict {
if([elementName isEqualToString:#"Root"]) {
//Initialize the array.
viewController.marray = [[NSMutableArray alloc] init];
}
else if([elementName isEqualToString:#"Element"]) {
//Initialize the hotel.
parsedObj = [[ParsedObj alloc] init];
//Extract the attribute here.
parsedObj.ID = [[attributeDict objectForKey:#"id"] integerValue];
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
if(!currentElementValue)
currentElementValue = [[NSMutableString alloc] initWithString:string];
else
[currentElementValue appendString:string];
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
if([elementName isEqualToString:#"Root"])
return;
//There is nothing to do if we encounter the Books element here.
//If we encounter the Book element howevere, we want to add the book object to the array
// and release the object.
if([elementName isEqualToString:#"Element"]) {
[accomodationController.marray addObject:parsedObj];
parsedObj = nil;
}
else {
NSString *cValue=[currentElementValue stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
[parsedObj setValue:cValue forKey:elementName];
}
[currentElementValue release];
currentElementValue = nil;
}
- (void)dealloc {
[parsedObj release];
[currentElementValue release];
[super dealloc];
}
#end
I tried a different approach and now I have two solutions and a problem with both of them.
I have a XML parser that should add objects to NSMutableArray from where it has been called at first. Parser is working OK, but here is the problem.
First approach:
I changed NSXMLParser's init method inside my XMLParser.m (mArray is for Mutable Array and):
- (XMLParser *) initXMLParser:(id)sender {
[super init];
mArray=(NSMutableArray *)sender;
return self;}
mArray in implementation file XMLParser.h:
#interface XMLParser : NSObject <NSXMLParserDelegate>{
NSMutableString *currentElementValue;
NSMutableArray *mArray;
Object *aObject;}
-(XMLParser *) initXMLParser:(id)sender;
#end
So, let's get to the part where we call XMLParser from ViewController.m:
mArray = [[NSMutableArray alloc] init];
NSURL *url = [[NSURL alloc] initWithString:#"http://www.a.com/some.xml"];
NSXMLParser *xmlParser = [[NSXMLParser alloc] initWithContentsOfURL:url];
XMLParser *parser = [[XMLParser alloc] initXMLParser:self.mArray];
[xmlParser setDelegate:parser];
[xmlParser parse];
...and here is ViewController.h:
#class XMLParser;
#interface AccomodationSecondViewController : UIViewController{
#public NSMutableArray *mArray;}
#property (nonatomic, retain) NSMutableArray *mArray;
I'm not sure because this is first time I'm using public objects so...
Anyway, this is the part inside XMLParser.m that should add objects:
//This is for closing bracket
if([elementName isEqualToString:#"Element"]) {
[mArray addObject:aObject];
[aObject release];
aObject=nil;}
So, the idea is to make mutable array mArray public inside ViewController and to send pointer to it to XMLParser. I know that this may be little unnatural, but I just want to get it to work.
My other idea is this:
To send pointer of ViewController to XMLParser and do the rest. So, I changed initXMLParser:
- (XMLParser *) initXMLParser:(id)sender {
[super init];
viewController=(ViewController *)sender;
return self;}
and this is the part inside ViewController.m where I call my method:
XMLParser *parser = [[XMLParser alloc] initXMLParser:self];
then I add object:
[viewController.mArray addObject:aObject];
Why is this not working??!!