0

I am parsing XML with NSXMLParser and I have some NSMutableArrays (instance variables) in my app. When I parse the XML file, I build objects with the values of the XML elements, and set my array with those objects.

In the moment that I set my array with those objects I can see the values of them. But once I have finished parsing the XML I want to retrieve those values and the array is now empty.

Here is a part of my code:

//STHelpViewController.h

#import <UIKit/UIKit.h>

@class SoftokeniPhoneAppDelegate, FAQ;
@interface STHelpViewController : UIViewController {
    UIView *viewAnswer;
    UINavigationBar *answerTitle;
    UIButton *answerDescription;
    NSArray *answersArray;
    NSMutableArray *faqs;     //THIS IS MY ARRAY WITH THE PROBLEM
    NSMutableString *currentElementValue;   
    FAQ *aFAQ;

    NSString* Answer1;
    NSString* Answer2;
    NSString* Answer3;
    NSString* Answer4;
}

@property (nonatomic, retain) IBOutlet  UIView *viewAnswer;
@property (nonatomic, retain) IBOutlet  UINavigationBar *answerTitle;
@property (nonatomic, retain) IBOutlet  UIButton *answerDescription;
@property (nonatomic, retain) NSMutableArray *faqs;

@property (nonatomic, retain)NSString* Answer1; 
@property (nonatomic, retain)NSString* Answer2; 
@property (nonatomic, retain)NSString* Answer3; 
@property (nonatomic, retain)NSString* Answer4; 


@end





//STHelpViewController.m


- (void)viewDidLoad {
    [super viewDidLoad];
    NSURL *url = [[NSURL alloc] initWithString:@"http://clientes.imit.cl/MovilPass/faq.xml"];
    NSXMLParser *xmlParser = [[NSXMLParser alloc] initWithContentsOfURL:url];

    //Initialize the delegate.
    STHelpViewController *parser = [[STHelpViewController alloc] initXMLParser];

    //Set delegate
    [xmlParser setDelegate:parser];

    //Start parsing the XML file.
    BOOL success = [xmlParser parse];

    if(success){
        NSLog(@"F.A.Q. loaded from XMl file");

        //HERE IS THE PROBLEM. AT THIS PART XCODE SAYS THAT faqs(my array) IS EMPTY

        Answer1 = [[[faqs objectAtIndex:0] valueForKey:@"Respuesta"]description];
        Answer2 = [[[faqs objectAtIndex:1] valueForKey:@"Respuesta"]description];
        Answer3 = [[[faqs objectAtIndex:2] valueForKey:@"Respuesta"]description];
        Answer4 = [[[faqs objectAtIndex:3] valueForKey:@"Respuesta"]description];
        answersArray = [[NSArray alloc] initWithObjects:Answer1,Answer2,Answer3,Answer4,nil];
    }
    else
    {
        NSLog(@"Error loading XML file for F.A.Q. Default data loaded instead.");
        answersArray = [[NSArray alloc] 
                        initWithObjects:@"El Movilpass es una nueva herramienta de Bci Móvil que le permitirá obtener la clave de  ocho dígitos desde su celular, sin tener que llevar el dispositivo electrónico. Regístrese para recibir la aplicación, cárguela en su Iphone y sincronícela.", 
                        @"El Movilpass es igual de válido que el dispositivo electrónico.  Sólo debe sincronizarlo correctamente en su celular, siguiendo uno a uno los pasos que le indiquen.",
                        @"Para sincronizar, presione el botón “Ajustes”, luego “Sincronizar” y obtendrá los siguientes pasos:\n \n Ingrese a su banca Internet. \n Entre a Seguridad y Emergencia. \n Click en Movilpass. \n Seleccione el botón 'Sincronizar'.",
                        @"Si se le perdió su TELEFONO, lo primero que debe hacer es llamar al 600 8242424 para que lo bloqueen. Una vez que obtenga el nuevo aparato, podrá solicitar nuevamente su Movilpass.", 
                        nil];   
    }                                   
}


- (STHelpViewController *) initXMLParser {

    [super init];

    //appDelegate = (SoftokeniPhoneAppDelegate *)[[UIApplication sharedApplication] delegate];

    return self;
}

- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName
  namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName
    attributes:(NSDictionary *)attributeDict {
    if([elementName isEqualToString:@"PreguntasFrecuentes"]) {
        faqs = [[NSMutableArray new]retain];        
    }
    else if([elementName isEqualToString:@"Item"]) 
    {
        aFAQ = [[FAQ alloc] init];
        aFAQ.itemID = [[attributeDict objectForKey:@"id"] integerValue];        
        NSLog(@"Leyendo valor de atributo id: %i", aFAQ.itemID);
    }
    NSLog(@"Procesando Elemento: %@", elementName);
}

- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {

    if(!currentElementValue)
        currentElementValue = [[NSMutableString alloc] initWithString:string];
    else
        [currentElementValue appendString:string];

    NSLog(@"Procesando Valor: %@", currentElementValue);

}

- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName
  namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {

    if([elementName isEqualToString:@"PreguntasFrecuentes"])
        return;
    if([elementName isEqualToString:@"Item"]) {
        [faqs addObject:aFAQ];
        [aFAQ release];
        aFAQ = nil; 
    }
    else
    {
        [aFAQ setValue:currentElementValue forKey:elementName];     
    }
    [currentElementValue release];
    currentElementValue = nil;

    //IF I PRINT MY ARRAY HERE, I CAN SEE THE VALUES
}

What could I be doing wrong?

Brad Larson
  • 170,088
  • 45
  • 397
  • 571
FelipeDev.-
  • 3,113
  • 3
  • 22
  • 32

2 Answers2

2

You instantiate faqs = [[NSMutableArray new]retain]; on every didStartElement call. It seems weird to me. You should move faqs = [[NSMutableArray new]retain]; to your viewDidLoad.

Moreover, I don't see the necessity of retain there.

knuku
  • 6,082
  • 1
  • 35
  • 43
  • Basically right: you keep creating new `faqs` instance variables with every `parser:didStartElement` method call. In addition, as your title notes you're using `faqs` as a bunch of new instance variables when I think you actually mean to use it as a property, in which case you should be referring to `self.faqs` when setting it. – Matthew Frederick Feb 11 '11 at 16:12
  • 1
    +1: it's totally wrong (the code, not your answer) because the object pointed to by faqs leaks every time `didStartElement` is called. And you are correct `new` gives you ownership, so there is no need for retain as well. – JeremyP Feb 11 '11 at 16:17
  • Here is nicely described difference between new and alloc init: http://stackoverflow.com/questions/719877/use-of-alloc-init-instead-of-new-objective-c – Bartosz Ciechanowski Apr 02 '11 at 21:27
  • Thx for ur replays. But I guess i not wrog, cuz i initialize my array only once. "PreguntasFrecuentes" is the root element of the xml file. Please take a look if([elementName isEqualToString:@"PreguntasFrecuentes"]) { faqs = [[NSMutableArray new]retain]; } //This will happen only once in the app life time. So, i'm still having the problem... Any ideas? – FelipeDev.- Feb 11 '11 at 17:52
1

1. In viewDidLoad you're allocating another STHelpViewController for no good reason. Do this instead,

    - (void)viewDidLoad {
        [super viewDidLoad];
        NSURL *url = [[NSURL alloc] initWithString:@"http://clientes.imit.cl/MovilPass/faq.xml"];
        NSXMLParser *xmlParser = [[NSXMLParser alloc] initWithContentsOfURL:url];
        [url release];

        //I am the delegate
        [xmlParser setDelegate:self];

        //Start parsing the XML file.
        BOOL success = [xmlParser parse];

        //NSLog(@"Count My FAQ %d", [[self faqs] count]); Uncomment for debugging.
        ...
    }

2. If you don't use self you have to fix initXMLParser. You must always use the designated initializer of you super class and assign to the ivar self. Like so,

    - (STHelpViewController *) initXMLParser {
        if(self = [super init]) {
            //alloc and init any ivar you want
        }
        return self;
    }

And then do this,

    - (void)viewDidLoad {
        [super viewDidLoad];
        NSURL *url = [[NSURL alloc] initWithString:@"http://clientes.imit.cl/MovilPass/faq.xml"];
        NSXMLParser *xmlParser = [[NSXMLParser alloc] initWithContentsOfURL:url];
        [url release];

        //Initialize the delegate.
        STHelpViewController *parser = [[STHelpViewController alloc] initXMLParser];

        //Set delegate
        [xmlParser setDelegate:parser];

        //Start parsing the XML file.
        BOOL success = [xmlParser parse];

        //Get results from parser
        NSArray parserFAQs = [parser faqs];
        [parser release];

        //NSLog(@"Count Parser's FAQ %d", [parserFAQs count]); Uncomment for debugging.
        ...
    }
Tobias
  • 4,397
  • 3
  • 26
  • 33