NSMutableArray property getting autoreleased? - objective-c

I have an NSMutableArray property defined in my Brain model.
#property (retain, nonatomic) NSMutableArray *stockColors;
The mutable array gets filled with objects during the Brain init.
In my view controller viewDidLoad function I do
*Brain brain = [[Brain alloc]init];
NSLog(#"Brain stockColors is %#", brain.stockColors);
This works well, and it logs a memory address as expected.
Then in the same view controller I have an "addButtonPressed" action declared linked to a UIButton in the .xib.
In my addbuttonPressed action if I try and do the exact same log
NSLog(#"Brain stockColors is %#", brain.stockColors);
The program crashes!
If I do
NSLog(#"Brain stockColors is %#", brain);
I get an address for the brain... so the brain exists.. but for some reason it seems that brain.stockColors is getting released or something by the time I get to the addButtonPressed action.
Can anyone explain what would cause it to get autoreleased?
Thanks

Considering you left out the important code... the actual Brain init call I am going to say that when you create your mutable array you are assigning to the instance variable and not the property.
//The Brain init method should contain something similar to this
self.stockColor = [NSMutableArray array];
//stockColor = [NSMutableArray array]; would be incorrect and get autoreleased

Related

Adding 'data' to an NSMutable Array - Xcode

I want to add 'email' data to my NSMutableArray so every time the button is pushed it adds another email to the array, so I end up with an array of email addresses but can't seem to get it working.
I think my problem is I am reinitialising the array so clearing it out and maybe need to put the initialisation somewhere else..which I have tried.
The below code lets me add an address but each time the IBAction is called I think I am clearing the array, any pointers or help would be great
#property (strong, nonatomic) NSMutableArray *mutable;
#synthesize mutable;
- (IBAction)array:(id)sender {
mutable = [[NSMutableArray alloc]initWithObjects:self.person.email, nil];
[mutable addObject:self.person.email];
NSLog(#"ARRAY OF EMAILS %#",mutable);
}
You are right - you are initializing the array each time the array: method is called. You can change the code to initialize it only once:
- (IBAction)array:(id)sender {
if(!self.mutable) {
self.mutable = [[NSMutableArray alloc]initWithObjects:self.person.email, nil];
}
[self.mutable addObject:self.person.email];
}
As a side note - you don't have to use #synthesize for this property. Read "Adopting Modern Objective-C" for more details.
Did you use UIViewController? If yes then reset the array in viewDidAppear method.
If you are using the UIView, then create the custom method to reset the array and call from the parent viewController.

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];

Why does my NSArray get deallocated?

I'm trying to understand Automatic Reference Counting, as I come from a high-level programming language (Python) and I'm working on a project which use this feature of Objective-C. I often get problems with ARC deallocating objects which I need later, but now I got a concrete example for which I hope I'll get an explanation.
- (void) animateGun:(UIImageView *)gun withFilmStrip:(UIImage *)filmstrip{
NSMutableArray *frames = [[NSMutableArray alloc] init];
NSInteger framesno = filmstrip.size.width / gun_width;
for (int x=0; x<framesno; x++){
CGImageRef cFrame = CGImageCreateWithImageInRect(filmstrip.CGImage, CGRectMake(x * gun_width, 0, gun_width, gun_height));
[frames addObject:[UIImage imageWithCGImage:cFrame]];
CGImageRelease(cFrame);
}
gun.image = [frames objectAtIndex:0];
gun.animationImages = frames;
gun.animationDuration = .8;
gun.animationRepeatCount = 1;
[gun startAnimating];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW,(arc4random() % 300)/100 * NSEC_PER_SEC), dispatch_get_current_queue(),^{
[self animateGun:leftGun withFilmStrip:[self getFilmStripForAction:gunShoot andTeam:nil withWeapon:nil]];
});
}
The idea behind this snippet of code is simple: I have a (UIImageView*)gun which I animate with the images stored in (NSMutableArray *)frames, at random times. (UIImage *)filmstrip is just an image which contains all the frames which will be used on animation. The first iteration of animation works, but the problems appears on the second iteration, where I get -[UIImage _isResizable]: message sent to deallocated instance ... or -[UIImage _contentStretchInPixels]: message sent to deallocated instance ... or -[NSArrayI release]: message sent to deallocated instance .... This happens at
gun.animationImages = frames;
but I don't understand why. I'm not requesting a fix for my issue, but just to help me understand what's happening here. Thanks.
ARC is a mechanism that removes the need to manually retain/release objects. Here's a nice site that explains how this works: http://longweekendmobile.com/2011/09/07/objc-automatic-reference-counting-in-xcode-explained/
Try changing "leftGun" for "gun". I think that's probably the one that gets deallocated at some point, if you're using it through an ivar. Otherwise, leftGun simply isn't in the scope.
Here's what it should look like:
In your .h file:
#property (nonatomic, strong) IBOutlet UIImageView *leftGun;
In your .m file:
dispatch_after(dispatch_time(DISPATCH_TIME_NOW,(arc4random() % 300)/100 * NSEC_PER_SEC), dispatch_get_current_queue(),^{
[self animateGun:gun withFilmStrip:[self getFilmStripForAction:gunShoot andTeam:nil withWeapon:nil]];
});
Also, not quite sure where "gunShoot" is coming from. Is that supposed to be an enum?
EDIT
Added an example of how the leftGun property should be defined. The reason behind using a property over an ivar is for memory management purposes. If you want to release or destroy an object that is a property, simply set it to nil and the property will take care of releasing the object if it has to.
You may prevent the deallocation of the frames array if you mark it as __block.
__block NSMutableArray *frames = [NSMutableArray array];
see “The __block Storage Type.”

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

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];

Table view not updating according to bindings

This is a very newbie question, and this is something I have done many times before, but there's something I'm missing this time.
In my AppDelegate.h file I declare an NSArray and set it as a property:
#interface AppDelegate : NSObject {
NSArray *lines;
}
#property(readwrite, retain) NSArray *lines;
#end
And then in the AppDelegate.m file in the awakeFromNib method I alloc it:
lines = [[NSArray alloc] init];
Then I have a method that sets the lines array:
NSString *fileContents = [NSString stringWithContentsOfFile:[NSHomeDirectory() stringByAppendingPathComponent:#"Desktop/sometextfile.txt"] encoding:NSUTF8StringEncoding error:NULL];
lines = [fileContents componentsSeparatedByString:#"\n"];
I have an array controller thats bound to AppDelegate.self.lines then I have a table column bound to Array Controller.arrangedObjects. I can confirm that the array is being updated (tested using NSLog) however the contents of the table are not being update (it remains blank).
Is there something obvious I'm missing here?
You don't need a data source if you're using Bindings. One or the other.
I have an array controller thats bound to "AppDelegate.self.lines" …
Why self?
#property(readwrite, retain) NSArray *lines;
No, use copy here. Otherwise, you'll find yourself retaining someone else's mutable array, which they will then mutate. Then “your” array will have changed without you knowing it.
Then I have a method that sets the lines array:
lines = [fileContents componentsSeparatedByString:#"\n"];
This is why the table doesn't show anything. You're not going through the property, you're accessing the instance variable directly. Direct instance variable accesses do not cause KVO notifications, so the array controller never finds out about the change.
Even worse, you're leaking the old array (since you simply assign over it without releasing it) and under-retaining this new array. Because you're not retaining the new array, that instance variable will hold a dead object shortly. The automatic retaining is done by the setLines: method, which only gets called when you call it.
You need to go through the property:
self.lines = [fileContents componentsSeparatedByString:#"\n"];
A property access is an implicit accessor message, so this both retains the array (or copies it, once you correct the #property as I suggested above) and posts KVO notifications.
When you say you have a arrangedObjects bound to the column do you mean you set the tablview datasource? If not you to set the tableview datasource to the lines array
You might want to read through this, it's got some good diagrams and explanations. What ennuikiller is saying is correct, I think it's a problem with your datasource. This is done by calling
[aTable setDelegate:aDelegate];