Obj-c: returning NSArray from method - objects out of scope - objective-c

My question is very similar to this question but with a few differences.
I have a class that takes in XML int the form of an NSString, parses out some objects and returns them in an NSArray somewhat like this:
//Parser.m
+(NSArray *)parseMessagesFromXml:(NSString *)xml
{
NSMutableArray *messages = [[NSMutableArray alloc] init];
//do some work parsing the xml
//for each <row>
// Message *m = makeMessageFromRow(row);
return [messages autorelease];
}
Then in my view controller class I declare an NSArray:
//MyViewController.h
NSArray *messages;
#property (nonatomic, retain) NSArray *messages;
and assign it using the above method:
//MyViewController.m
messages = [Parser parseMessageFromXml:xml];
[[self tableView] reloadData];
So here comes the problem: when i assign the array to messages it has elements in it, but they are all "out of scope." I have debugged the problem and I know that the parsing method is correctly creating the objects and adding them to the NSMutableArray before returning it. When I try to access the objects in messages my app crashes and the debugger says EXC_BAD_ACCESS. What is more peculiar is this: if i store the array into a local variable it works just fine:
NSArray *temp = [Parser parseMessageFromXml:xml];
//temp has all the right objects and they are in scope
messages = temp;
//messages has the objects, but cannot access them (they are out of scope).
It is as if I can legally view them in a local scope, but i cannot assign them to a member variable. I have even tried iterating over the returned array, adding each one to messages individually, but the result is the same: they are out of scope. I am totally clueless on this one.
What is it about messages as a member variable that doesn't allow it to hold these objects?

The problem is that the array is being released. When you call autorelease in parseMessagesFromXml:, you tell the array that it should be released sometime in the future. This is happening before the table reloads its data. You need to retain the array again to prevent it from being released. In this case, it is as simple as using the accessor methods to set your property instead of setting the instance variable directly.
//MyViewController.m
self.messages = [Parser parseMessageFromXml:xml];
[[self tableView] reloadData];

Related

Trying to add entries in NSArray to NSDictionary, but values in NSDictionary are getting deallocated

I have three Deck Objects which are mostly just wrappers around NSArray objects containing Card objects. Initially, all the Card objects are in deck1, and eventually move through deck2 and deck3.
I also have an NSDictionary object that maps NSString objects to Card objects. I use the cards in deck1 to build this lookup table at the beginning of the game like this...
-(NSDictionary *)buildLookupTable {
NSMutableDictionary *lookup = [[NSMutableDictionary alloc] init];
for (Card *card in self.cards) {
NSString *lookupCode = [self buildCode:card];
[lookup setObject:card forKey:lookupCode];
}
return lookup;
}
I then pass the NSDictionary and the Decks to a layer object, but when I attempt to lookup a given Card based on a NSString, I get "-[CFString hash]: message sent to deallocated instance", but I can easily find the Card I'm looking for, not deallocated, in deck1 or deck2.
The NSString I'm using as a key to retrieve the desired value isn't deallocated, nor is the NSDictionary itself. I have even iterated over the return from the NSDictionary object's allValues method, and none of those are deallocated either.
What other deallocated objects could there be?
Edit-version2:
I've narrowed it down a bit.
In the lookup code this works
NSString *key = #"4-0"; //(NSString *)sprite.userData;
return [self.cardLookup objectForKey:key];
but this doesn't
NSString *key = (NSString *)sprite.userData; // value is #"4-0"
return [self.cardLookup objectForKey:key];
In the debugger sprite.userData looks fine.
sprite.userData is defined in the Card class buildSprite method as...
sprite.userData = [NSString stringWithFormat:#"%i-%i",self.x, self.y];
It looks like calling copyWithZone on key doesn't work (like it would for any other NSString).
Found the issue!
The problem was with the sprite.userData object and how it was created.
sprite.userData = [NSString stringWithFormat:#"%i-%i",self.x, self.y];
Evidently sprite.userData was getting released (thought the Xcode debugger seemed to know what it was, possibly because I had zombie objects enabled). The correct version is...
sprite.userData = [[NSString stringWithFormat:#"%i-%i",self.x, self.y] retain];

I can't add my data object to my NSArray

So I have this method:
-(void)addLaneToRacingLanes:(UITapGestureRecognizer*)sender{
laneDataObject *data=[self.laneDataObjects objectAtIndex:sender.view.tag];
[self.racingLanes addObject:data];
NSLog(#"%i",self.racingLanes.count);
[sender.view setBackgroundColor:[UIColor yellowColor]];
}
It uses the tag from the senders view to find out which data object corresponds to that view.I'm using this to add to my racingLanes which is how I update these views, but my problem is that for some reason I cant add my laneDataObjects to my array racingLanes. Any ideas?
This is how the properties are set up:
#property (strong,nonatomic)NSArray *laneDataObjects;
#property (strong,nonatomic) NSMutableArray *racingLanes;
I have already run through the tags and they all work. The tags work such that lane 1 is tag 0 with its data object at 0, then lane 2 is tag 1 and its data is 1, so on and so forth. I already pre-tested this. And I have checked that both the laneDataObject array has been properly set up. Is it because my racingLanes isn't using a custom getter or setter? How would I go about changing that?
Incase it matters I used
NSLog(#" %i",self.racingLanes.count);
to find out if the array was empty.
It is a near certainty that the racingLanes has not been initialized: since the objects that you are adding are non-nil (you'd see an exception thrown otherwise) the racingLanes must be nil then.
You need to set racingLanes to NSMutableArray in the designated initializer:
_racingLanes = [NSMutableArray array];
Did you make sure to initialize your NSMutableArray in your class's -init or -viewDidLoad function?
// WITH ARC
self.racingLanes = [NSMutableArray array];
// WITHOUT ARC
self.racingLanes = [[NSMutableArray alloc] init];

big memory problem in objective c

i've a function like this:
#property(nonatomic,retain) NSMutableArray *array;
#synthesize array = _array;
(NSMutableArray *) name
{
self.array = [[NSMutableArray alloc]init];
[_array addObject:object];
[object release];
return [_array autorelase];
}
In the other function i've a property like the property above, named result, and i make:
self.result = [... name];
Then in dealloc i make
[_result release];
and it crashes in this point, how can i solve this?
I've tried many roads, but or it crashes, or i see memory leak in Instruments, where am i wronging?
Thanks.
While there's a lot wrong with this code, the likely cause of your crash is that you're releasing object within -name without taking ownership of it- unless you're creating object within the method through a call to -alloc, -new, or -copy, that method doesn't own it and isn't responsible for releasing it. This is causing that object to be invalid within the NSMutableArray, so when _result releases, it attempts to release an invalid piece of memory and crashes.
Also, properties aren't simply local variables for individual functions, they're member variables for instances of the class for which you're writing these classes. If your end goal is only to return an autoreleased array and set it to result you could do the following:
- (NSMutableArray *) name {
//call a convenience method- it comes back autoreleased
NSMutableArray* theArray = [NSMutableArray array];
[theArray addObject:object];
//don't release object unless you took ownership of it in this function
return theArray;
}
then outside the function, either call self.result = [... name] or [self setResult:[... name]];
You have a very strange method definition (the header should have a - before the return type), and inside that definition you are accessing a variable called object that doesn't seem to exist. I'm not sure what you want, but you've got at least one memory problem. The array that you create in name gets leaked every time the method is called. If you add some details, like the crash message, someone may be able to help more.

NSMutableDictionary error

I want to use NSMutableDictionary to cache some data i will use later. My custom object is following:
#interface MyData : NSObject {
NSRange range;
NSMutableArray *values;
}
#property (nonatomic, retain) NSMutableArray *values;
and implement:
- (id)init {
if (self = [super init]) {
values = [[NSMutableArray alloc] init];
}
return self;
}
and when i wanna cache it, i use it like this:
NSMutableDictionary *cache = [[NSMutableDictionary alloc] init];
NSString *key = #"KEY";
MyData *data = [[MyData alloc] init];
// save some data into data
[data.values addObject:"DATA1"];
[data.values addObject:"DATA2"];
//... ...
[cache setObject:data forKey:key];
My questions is the count of cache.values is zero when i retrieve this object later as follow:
[cache objectForKey:#"KEY"];
i can retrieve "data" and the object's memory address is the same as the address when i put it into cache.
what's wrong? i need some kind guys help, any info is helpful. thanks
As Carl Norum pointed out, you're passing C strings to addObject:. addObject:, as its name suggests, requires a pointer to a Cocoa object; a C string is a pointer to characters. You need to pass NSString objects there; for literal strings, this simply requires prefixing them with #: "Fred" is a constant C string, whereas #"Fred" is a constant NSString object.
Is cache an instance variable? It looks like it's not; it appears to be a local variable, which means you're creating a new dictionary object every time. That's why there's nothing you've added previously (to previous dictionaries) in the new one. It also means you're leaking those previous dictionaries, since you're not releasing them (not in the code you showed, anyway).
Make cache an instance variable and only create the dictionary when you don't already have one (i.e., when cache == nil). Creating the dictionary in your init method is one good way. And make sure you manage its lifetime appropriately, so you don't leak and/or crash.
First of all your objects your adding don't look right it should have an # before the string. Like #"DATA1"
Second when you add an object to a dictionary or an array it does not make an actual copy of it. It just creates a pointer to it so if those objects are destroyed or moved somewhere also they are also gone out of your dictionary. A better way to make a cache of your values would be to copy the objects like so:
MyData* cache = [[MyData alloc] init];
for (int i = 0; i < [data.values count]; i ++){{
[cache.values addObject:[NSString stringWithString:[data.values objectAtIndex:i]]];
}
Don't use a dictionary in this situation.

simple NSMutable array question

umm So simple question here:
I have an instance of NSMutableArray declared in my header
NSMutableArray *day19;
#property (nonatomic, retain) NSMutableArray *day19
implementation:
#synthesize day19;
In my viewDidLoad
self.day19 = [[NSMutableArray alloc] init];
In the myMethod where I want to add objects to the array I:
NSObject *newObject = [[NSObject alloc] init];
[day19 addObject:newObject];
However... when i check the day19 array there is nothing in it. If I conversely add the newObject to a tempArray within the myMethod scope and then set the day19 array to the tempArray, day19 has the objects.
Super basic I know just must be a confused morning or something...
thanks for any help
Is day19 actually an instance variable? In the snippet, it's not clear when it's declared as an instance variable or just as a variable outside the scope of the class.
A couple of things:
Are you sure viewDidLoad is the right place to init your array? Confer here.
Also, at least from the code you've got posted, it looks like you're being sloppy with your retains. If your property is a retain type, you should not be writing:
self.myProperty = [[Something alloc] init]; // double retain here, bad
You should instead be writing something like:
self.myProperty = [[[Something alloc] init] autorelease]; // single, good
Also, with
NSObject *newObject = [[NSObject alloc] init];
[day19 addObject:newObject];
unless you have a
[newObject release];
down the pike, you've got a memory leak.
In my viewDidLoad
self.day19 = [[NSMutableArray alloc] init];
In the myMethod where I want to add objects to the array I:
NSObject *newObject = [[NSObject alloc] init];
[day19 addObject:newObject];
However... when i check the day19 array there is nothing in it. If I conversely add the newObject to a tempArray within the myMethod scope and then set the day19 array to the tempArray, day19 has the objects.
Let me guess: You checked the array with code like this:
NSLog(#"day19 contains %lu objects", [day19 count]);
Remember that a message to nil does nothing and returns nil, 0, or 0.0. That's why the output said 0 objects: You don't have an array in the first place. The most probable reason for that is that viewDidLoad hasn't been called yet, so you have not yet created the mutable array.
It's also possible that you have an array (i.e., the view has been loaded) at the time you examine the array, but you didn't have an array yet (the view hadn't been loaded yet) at the time you tried to add to the array, so your addObject: message fell on deaf ears.
Consider creating the array earlier. You probably should be creating it in init or initWithCoder:.
A third possibility is that you examined the array before you ever added to it. Make sure you log or break at both points, so you know which one happened first.
Whatever the problem is, you also need to either assign the array to the instance variable, not the property, or autorelease the array before assigning it to the property. Otherwise, you're over-retaining the array, which means you will probably leak it later on. You probably need to review the Memory Management Programming Guide for Cocoa.