views:

28

answers:

2

I have a data structure object which is loaded from an XML file inside the "did finish loading" method of the delegate.

If I use the data objects "printData" method from inside the load method of the delegate it shows all the info. I can do this multiple times.

However, I have a button which runs a second method in the delegate and all this does is run the printData method of my data object but all I get is either nothing or some random property text.

e.g. "UIDeviceFamily" or "ar.lproj" etc...

Once this has printed it crashes the app with an "EXC_BAD_ACCESS" error.

I remember there being a directory to look into find more details of this error but I can't remember which one it is.

Thanks for any help you can provide.

Sorry, this is the code that matters in the app...

This is the code that populates the data.

    -(id)initMapFromXMLData:(NSData *)xmlData
{
    if (self = [super init])
    {
        MapXMLParser *parser = [[MapXMLParser alloc] initWithData:xmlData];
        [parser setDelegate:parser];
        [parser parse];

        statementPairs = [NSDictionary dictionaryWithDictionary:parser.statementPairs];
        prefPairs = [NSDictionary dictionaryWithDictionary:parser.prefPairs];
        clusters = [NSDictionary dictionaryWithDictionary:parser.clusters];

        /*
        statementPairs = parser.statementPairs;
        prefPairs = parser.prefPairs;
        clusters = parser.clusters;
        [parser release];*/
    }
    return self;
}

This then gets run from the delegate...

NSData *xmlData;

xmlData = [[NSData alloc] initWithContentsOfFile:@"/Users/oliver/Documents/XCode stuff/Saviio/Saviio 2/Saviio/Classes/Statements"];

myMap = [[Map alloc] initMapFromXMLData:xmlData];

And then in the same delegate method I display it...

[myMap printClusters];

... which runs...

-(void)printClusters
{
    NSLog(@"Printing Clusters for %@", self);

    for (int i = 1; i <= [clusters count]; i++)
    {
        Cluster *tempCluster;

        tempCluster = [clusters objectForKey:[NSString stringWithFormat:@"%d", i]];

        NSLog(@"Cluster %@", tempCluster.name);

        for (int j = 0 ; j < [tempCluster.fgids count]; j++)
        {
            Preference *tempPref;

            tempPref = [prefPairs objectForKey:[[tempCluster.fgids objectAtIndex:j] stringValue]];

            NSLog(@"Pref ---> %@/%@", tempPref.left, tempPref.right);

            for (int k = 0 ; k < [tempPref.qids count]; k++)
            {
                Statement *tempStat;

                tempStat = [statementPairs objectForKey:[[tempPref.qids objectAtIndex:k] stringValue]];

                NSLog(@"Stat -------> %@ - %@", tempStat.left, tempStat.right);
            }
        }
    }

    NSLog(@"END");
}

This all works fine.

However, when I then run the same method [myMap printClusters] from a method that runs from a button press in the delegate then it prints the first line... "Printing clusters for ".

And then the strange line and then crash.

+2  A: 

It appears that clusters is a property of self but you never retain it. Since it is created with a convenience method that returns an autoreleased dictionary it is released when the methods scope completes. When you call it a second time, it is not alive any longer.

Change:

clusters = [NSDictionary dictionaryWithDictionary:parser.clusters];

...to:

self.clusters = [NSDictionary dictionaryWithDictionary:parser.clusters];

...or:

clusters = [[NSDictionary dictionaryWithDictionary:parser.clusters] retain];

... and then change all other references to clusters to self.clusters. Do the same for any other properties.

TechZen
Thanks for the tip!You made me think about allocating stuff rather than using the input variables and I found some other places and managed to sort it!Thanks!
Fogmeister
A: 

Well, following the advice of TechZen I had a look at the code that was populating the NSDictionary and found that I was storing the pointed parser variables.

Instead I changed them to allocate its own variables and store them instead.

That sorted it.

Woo!

Old code...

-(id)initWithName:(NSString *)inputName
{
    if (self = [super init])
    {
        name = inputName;
        fgids = [[NSMutableArray alloc] initWithCapacity:1];
    }
    return self;
}

New code...

-(id)initWithName:(NSString *)inputName
{
    if (self = [super init])
    {
        self.name = [NSString stringWithString:inputName];
        self.fgids = [[NSMutableArray alloc] initWithCapacity:1];
    }
    return self;
}

Woo!

Fogmeister
The correct way to accept an answer is... to accept the answer.
tc.
Well, it wasn't technically the correct answer as the actual fix was nothing to do with the line you specified and I never had to use any retain properties (in fact, they didn't work). However, I did vote it up as it was helpful in finding the actual fix as it got me thinking along the right lines.
Fogmeister