Share NSArray Contents between multiple methods in a single class - objective-c

What am I doing wrong? My code crashes when I try to log the array. Here is my class:
#interface ArrayTestAppDelegate : NSObject <UIApplicationDelegate> {
UIWindow *window;
NSArray *array;
}
#property (nonatomic, retain) IBOutlet UIWindow *window;
#property (nonatomic, retain) NSArray *array;
-(IBAction)buttonPressed;
#end
#implementation ArrayTestAppDelegate
#synthesize window, array;
- (void)applicationDidFinishLaunching:(UIApplication *)application {
array = [NSArray arrayWithObjects:#"Banana", #"Apple", #"Orange", #"Pear", #"Plum", nil];
[window makeKeyAndVisible];
}
-(IBAction)buttonPressed {
NSLog(#"%#", array);
}
- (void)dealloc {
[window release];
[array release];
[super dealloc];
}
#end

This is a common memory management error in Cocoa. The arrayWithObjects method of the NSArray class returns an autoreleased object. By the time you try to log the array in the buttonPressed method, the array has already been released and you get a crash. The fix is easy:
array = [[NSArray alloc] initWithObjects:#"Banana", #"Plum", nil];
Or:
array = [[NSArray arrayWithObjects:#"Banana", #"Plum", nil] retain];
I guess the first one is better, the retain on the end of the second example is easy to miss. I would suggest that you read some more on memory management in Cocoa.

Related

NSArrayController does not update content for second window

I'm writing an OSX app where a second window is opened to show results when the button on the first window is pushed. The window-2 start fine and shows what I want. But when I change inputs in window-1 and hit the action button again the window-2 doesn't update the results.
here my questions:
how does the content of window-2 update after input change in window-1
how is window-2 closed and released (right now window-2 shows up with the same content before closed when action button is pushed again)
here is the code for the action button:
- (IBAction)pushRun:(id)sender {
if (!rwc)
{
rwc = [[ResultWindowController alloc] init];
[rwc setValueArray:[toDoItemArrayController arrangedObjects]];
[rwc setNumberOfCalculations:[NSNumber numberWithInt:[_inputNumberOfCalculations intValue]]];
[rwc calculateResults]; //starts method in 2nd-window controller for result calculation
}
[rwc showWindow:self];
}
It might be easy but I'm afraid to always create an other ResultWindowController instance.
Thanks in advance.
Joerg
here is the ResultWindowController.h:
#import <Cocoa/Cocoa.h>
#interface ResultWindowController : NSWindowController{
NSArray *valueArray;
NSMutableArray *resultArray;
NSNumber *numberOfCalculations;
}
#property (nonatomic, retain, readwrite) NSArray *valueArray;
#property (retain) NSNumber *numberOfCalculations;
#property (nonatomic, retain, readwrite) NSMutableArray *resultArray;
-(void)calculateResults;
#end
and here the ResultWindowController.m
#import "ResultWindowController.h"
#import "ResultItem.h" //my result model
#implementation ResultWindowController
#synthesize valueArray, resultArray, numberOfCalculations;
- (id)init
{
if(![super initWithWindowNibName:#"ResultWindow"])
return nil;
return self;
}
-(void)awakeFromNib
{
}
- (void)windowDidLoad
{
[super windowDidLoad];
}
- (void)calculateResults
{
//a lot of calculation code ...
ResultItem *newResult = [[ResultItem alloc]init];
[newResult setValue:[nameArray objectAtIndex:i] forKey:#"name"];
[newResult setValue:[NSNumber numberWithDouble:avg] forKey:#"averageValue"];
[newResult setValue:[NSNumber numberWithDouble:min] forKey:#"minValue"];
[newResult setValue:[NSNumber numberWithDouble:max] forKey:#"maxValue"];
[newResult setValue:dimensionRandomArray forKey:#"randomArray"];
[resultArray addObject:newResult];
}
resultarray is the content source for an arraycontroller in the ResultWindowController.xib. The arraycontroller is bound to a table view which is supposed to show the array content. This is not updated the second time.
I believe you must simply do the following:
- (IBAction)pushRun:(id)sender {
if (!rwc)
{
rwc = [[ResultWindowController alloc] init];
[rwc setValueArray:[toDoItemArrayController arrangedObjects]];
[rwc setNumberOfCalculations:[NSNumber numberWithInt:[_inputNumberOfCalculations intValue]]];
[rwc calculateResults]; //starts method in 2nd-window controller for result calculation
}
[rwc setValueArray:[toDoItemArrayController arrangedObjects]];
[rwc setNumberOfCalculations:[NSNumber numberWithInt:[_inputNumberOfCalculations intValue]]];
[rwc calculateResults]; //starts method in 2nd-window controller for result
[rwc showWindow:self];
}
You were only changing values of ResultWindowController if it did not exist. You want to change values no matter what, just not start a new instance. So, as long as you don't use [[alloc]init] you're good.
Hope that helps. If you need anything else, drop a comment.
Edit
To create a property for your tableView do the following:
In .h
#import <Cocoa/Cocoa.h>
#interface ResultWindowController : NSWindowController{
NSArray *valueArray;
NSMutableArray *resultArray;
NSNumber *numberOfCalculations;
}
#property (nonatomic, retain, readwrite) NSArray *valueArray;
#property (retain) NSNumber *numberOfCalculations;
#property (nonatomic, retain, readwrite) NSMutableArray *resultArray;
#property (assign) NSTableView *yourTableView; //Add this code
-(void)calculateResults;
#end
and here the ResultWindowController.m
#import "ResultWindowController.h"
#import "ResultItem.h" //Your result model
#implementation ResultWindowController
#synthesize valueArray, resultArray, numberOfCalculations;
#synthesize yourTableView; //Add this code
Then you should be able to call [yourTableView reloadData]

iOS Passing (Retain) the value of an NSMutableArray to another NSMutableArray in another view

I'm using .XIB and without ARC. I'm passing the value of the NSMultableArray to another view, if I put [self presentModel...], it works, but if I call the AnotherView with a button the value of the NSMultableArray of the AnotherView is null!
AnotherView.h
#interface AnotherViewController : UIViewController<UITableViewDataSource, UITableViewDelegate>{
NSMutableArray *otherAnother;
NSMutableArray *arrayOfTheAnotherView;
}
#property (retain, nonatomic) IBOutlet UITableView *tableView;
#property (retain, nonatomic) NSMutableArray *arrayOfTheAnotherView;
AnotherView.m
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
otherAnother = [[NSMutableArray alloc]init];
otherAnother = [[NSMutableArray alloc]initWithArray:self.arrayOfTheAnotherView];
//    [otherAnother addObjectsFromArray:arrayOfTheAnotherView];
NSLog(#"%#", self.arrayOfTheAnotherView);
}
The NSLog has written "null"
CurrentView.h
#interface CurrentViewController : UIViewController {
NSMutableArray * arrayCurrentView;
AnotherViewController *superAnotherView;
}
#property (retain, nonatomic) AnotherViewController *superAnotherView;
CurrentView.m
#synthesize superAnotherView;
NSString *x = [[NSString alloc]initWithFormat:#"%#",[label text]];
arrayCurrentView = [[NSMutableArray alloc]init];
[arrayCurrentView retain];
[arrayCurrentView addObject:x];
self.superAnotherView = [[AnotherViewController alloc]initWithNibName:nil bundle:nil];
self.superAnotherView.arrayOfTheAnotherView = [[NSMutableArray alloc]init];
[self.superAnotherView.arrayOfTheAnotherView retain];
[self.superAnotherView.arrayOfTheAnotherView addObjectsFromArray:arrayCurrentView];
I don't know how to retain the value of the NSMultableArray, thanks the help.
It is how I call the AnotherView:
UIButton *buttonAnother = [UIButton buttonWithType:UIButtonTypeCustom]; [buttonAnother setTag:5]; [buttonAnother addTarget:self action:#selector(switchTabBar:) forControlEvents:UIControlEventTouchDown];
[tabBarViewController.view addSubview:buttonAnother];
- (IBAction)switchTabBar:(id)sender { switch ([(UIButton *)sender tag]) { case 5: [self.tabBarController setSelectedIndex:0]; break; }
These should not be necessary:
self.superAnotherView.arrayOfTheAnotherView = [[NSMutableArray alloc]init];
[self.superAnotherView.arrayOfTheAnotherView retain];
Properties are already initialized when the class is initialized.
In fact, your explicit retain lines shouldn't be necessary at all; you're not releasing them as far as I can see and just making the retain count 2 so that it has to be released twice.
I think there's something going on here that the methods and view controllers are not being called in the order you want, probably something to do with the tab bar.
I dicoverd! I'm using the MVC
A array of the APP delegate don't lost its value.
AppDelegate
NSMutableArray *arrayDelegate;
View.m
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication]delegate];
[appDelegate.arrayDelegatePedido addObject:#"title"]; //for example

Yet another EXC_BAD_ACCESS - However with zombie (instruments), never exe_bad_access

I have spent hours on this and read everybit on the web about memory management, zombies. leaks (tried instruments). But I cannot figure this one out. Does someone has a clue? I am getting a EXC_BAD_ACCESS at the following like of code on popping the ChildViewController.
[profileVO release]; profileVO = nil;
I strongly feel I have followed all memory management best practices!
Details:
I have a model file. (CachedProfileVO)
CachedProfileVO.h
#interface CachedProfileVO : NSObject {
NSString *name;
NSString *email;
}
#property (nonatomic, retain) NSString *name;
#property (nonatomic, retain) NSString *email;
#end
CachedProfileVO.m
#synthesize name, email;
- (void) dealloc
{
[super dealloc];
[name release]; name= nil;
[email release]; email = nil;
}
Now I have a UINavigationController. The ParentViewController and the ChildViewController.
I invoke the ChildViewController as follows:
[self.navigationCntroller pushViewcontroller:childViewcontroller animated:YES];
In the ChildViewController, I basically use the model CachedProfileVO. However when this view controller is popped (back button on UI), it gives a EXC_BAD_ACCESS
ChildViewController.h
#interface ChildViewcontroller : UITableViewController {
CachedProfileVO *profileVO;
}
#property (nonatomic, retain) CachedProfileVO *profileVO;
#end
ChildViewController.m
#synthesize profileVO;
- (void) dealloc
{
[super dealloc];
[profileVO release]; profileVO = nil; ****** GETTING EXE_BAD_ACCESS here
}
- (void) viewDidLoad
{
CachedProfileVO *vo = [CachedProfileVO alloc] init];
self.profileVO = vo;
[vo release];
}
//responseString looks like this: [Murdoch, murdoch#email.com][other data][more data]
- (void) populateProfile:(NSString *) responseString
{
NSMutableString *str = [[NSMutableString alloc] initWithCapacity:20];
[str setString:responseString];
[str deleteCharactersInRange: NSMakeRange(0,1)];
[str deleteCharactersInRange: NSMakeRange([str length]-1,1)];
NSArray *tempArray = [str componentsSeparatedByString: #"]["];
NSString *tempStr = (NSString*)[tempArray objectAtIndex:0];
NSArray *bioArray = [tempStr componentsSeparatedByString:#","];
self.profileVO.name = (NSString*)[bioArray objectAtIndex:0];
self.profileVO.email= (NSString*)[bioArray objectAtIndex:1];
[str release]; str = nil;
}
Note that the function populateProfile is called after some event. I know it is called. And dealloc then causes the problem. Also this does not happen in every pop. I have to try several times to reproduce. It is never reproduced using zombies in instruments!!!
You are calling [super dealloc]; first in your examples. That should always be the last call otherwise you are accessing instance variables that belong to a now deallocated class. The following should work fine if you followed memory management rules elsewhere.
- (void) dealloc
{
[profileVO release];
[super dealloc];
}

Gaining access to an NSMutableArray

I am new to arrays and objects. I have a class HowToPlay.h (of course that has a .m also) their i define two arrays
NSMutableArray *nibs;
NSMutableArray *unusedNibs;
#property (nonatomic, retain) NSMutableArray *nibs;
#property (nonatomic, retain) NSMutableArray *unusedNibs;
then we jump into the .m,
i write all the stuff for the arrays,
nibs = [[NSMutableArray alloc]initWithObjects:#"Question 2", #"Question 3", nil];
self.unusedNibs = nibs;
[nibs release];
Then i also have another class called Question 1, i need to be able to use this array in that class, and be able to change it, but keep the changes on the HowToPlay.m file.
here is why, basically this array loads random NIB files, and then deletes them from the array when they have been used.
in Question 1.m here is what im doing to use the array
random = arc4random() % [self.unusedNibs count];
NSString *nibName = [self.unusedNibs objectAtIndex:random];
[self.unusedNibs removeObjectAtIndex:random];
if (nibName == #"Question 3") {
Question_3 *Q3 = [[Question_3 alloc] initWithNibName:#"Question 3" bundle:nil];
Q3.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:Q3 animated:YES];
[Q3 release];
}
if (nibName == #"Question 2") {
Question_2 *Q2 = [[Question_2 alloc] initWithNibName:#"Question 2" bundle:nil];
Q2.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:Q2 animated:YES];
[Q2 release];
}
This all seems to work fine, but the problem im having is it seems that objects in the array are not getting deleted, even though this line runs
[self.unusedNibs removeObjectAtIndex:random];
I haved tried making it
[HowToPlay.unusedNibs removeObjectAtIndex:random];
I get a error saying expected '.' before '.' token
It seems to me that i have access to read the array, but not to change it. any way to fix this so i can change the array? thanks
UPDATE:
here is the whole HowToPlay.h file contents:
#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
int score;
int usedQ1;
int usedQ2;
int usedQ3;
NSMutableArray *nibs;
NSMutableArray *unusedNibs;
#interface HowToPlay : UIViewController <UIPickerViewDelegate, UIPickerViewDataSource> {
UIPickerView *selectType;
NSMutableArray *selectArray;
AVAudioPlayer *audioPlayer;
UIActivityIndicatorView *progress;
UIButton *worldButton;
UIButton *politicsButton;
UIButton *starButton;
}
#property (nonatomic, retain) NSMutableArray *nibs;
#property (nonatomic, retain) NSMutableArray *unusedNibs;
#property (nonatomic, retain) IBOutlet UIButton *worldButton;
#property (nonatomic, retain) IBOutlet UIButton *politicsButton;
#property (nonatomic, retain) IBOutlet UIButton *starButton;
#property (nonatomic, retain) IBOutlet UIPickerView *selectType;
#property (nonatomic) int usedQ1;
#property (nonatomic) int usedQ2;
#property (nonatomic) int usedQ3;
#property (readwrite) int score;
-(IBAction)World:(id)sender;
- (IBAction)Politics:(id)sender;
-(IBAction)Stars:(id)sender;
#end
#import "MainViewController.h"
#import "Question 1.h"
#import "Question 2.h"
#import "Question 3.h"
I import after because otherwise i get errors
Also I have the array set up on theViewDidLoad part of HowToPlay, is this a bad idea?
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor blackColor];
NSURL *click = [NSURL fileURLWithPath:[NSString stringWithFormat:#"%#/click.wav", [[NSBundle mainBundle] resourcePath]]];
audioPlayer = [[AVAudioPlayer alloc]initWithContentsOfURL:click error:nil];
audioPlayer.numberOfLoops = 1;
progress = [[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
progress.frame = CGRectMake(0.0, 0.0, 30.0, 30.0);
progress.center = self.view.center;
[self.view addSubview: progress];
usedQ1 = 0;
usedQ2 = 0;
usedQ3 = 0;
selectArray = [[NSMutableArray alloc]init];
[selectArray addObject:#"World"];
[selectArray addObject:#"Politics"];
[selectArray addObject:#"Stars"];
score = 0;
nibs = [[NSMutableArray alloc]initWithObjects:#"Question 2", #"Question 3", nil];
self.unusedNibs = nibs;
[nibs release];
}
How do you check if objects are actually deleted? Please post some more code as the way you remove objects from unusedNibs seems to be fine. As per this code:
[self.unusedNibs removeObjectAtIndex:random];
and this code
[HowToPlay.unusedNibs removeObjectAtIndex:random];
The reason why you're getting this error is because you're trying to access unusedNibs using a class name "HowToPlay" instead of its instance "self".
A couple of things: first, to call properties like unusedNibs in HowToPlay it looks like you are calling the class and not an instance. You need to create a HowToPlay object and assign it to a property in your Question1 object so the question one Object has something to call to. Question1 should not call 'self' for unusedNibs since it does not own it.
With what you are doing it might make more sense to put the function figuring out the next random controller in the HowToPlay class. That way your question view controllers could just ask it to return which controller is next without having to duplicate that code across every question controller.

Objective-c addObject in loop causes memory leak

I have found a similar issue:
NSMutableArray addObject in for loop - memory leak
But none of those suggestions seem to fix my problem.
I have a simple loop where I'm creating an object and adding it to an array. When I try to release the object at the end of each loop the app crashes with "EXC_BAD_ACCESS". If I don't release the object I get leaked memory:
In .h
NSMutableArray *mainlist;
...
#property (nonatomic, retain) NSMutableArray *mainList;
In .m
#synthesize mainlist;
...
for (int i = 0; i < [self.objects count]; i++) {
MyObj *myObj = [[MyObj alloc] init];
myObj.title = [[self.objects objectAtIndex: i] valueForKey: #"title"];
[self.mainlist addObject:myObj];
[myObj release]; // crashes with release
}
MyObj just has some properties:
#property (nonatomic, retain) NSString *title;
#property (nonatomic, retain) NSString *date_text;
...
#synthesize title;
#synthesize date_text;
- (void)dealloc
{
[super dealloc];
[title release];
[date_text release];
}
#end
Any help would be much appreciated.
Thanks.
Crashes cause you first call dealloc of superclass and then try to release attributes. Change this to:
- (void)dealloc
{
[title release];
[date_text release];
[super dealloc];
}
And also: I'm almost certain that your self.mainlist is nil, when you're adding objects there. Creating a property doesn't mean that the attribute would be initialized automatically.