NSArray losing data in objective-c, Could someone explain why? - iphone

I have an nsarray that when I NSLog it from one of my methods (inside WorkOutList.m) I can see its contents, but when I try to look inside it from a different method inside WorkOutList.m it seems to be empty. I am aware that my memory management needs some work, could anyone help explain whats happening here?
I am using popViewControllerAnimated:YES to return the view from a tableView back to a view controller, but just before I do that I set my array in a method inside WorkOutList. When I NSLog that array from that same method I am returned results, however when i NSLog it from else where it is returned empty.
I have been told that it might be the viewDidLoad method where the array is init, but that the other array in that method customWorkouts still retains its data. So i dunno, any explanation would be really helpful. I want this to work, but I also really want to understand it so I can get on with coding correctly.
Thanks so much!
WorkOutList.h
#import <UIKit/UIKit.h>
#interface WorkOutList : UIViewController {
NSManagedObjectContext *managedObjectContext;
NSMutableArray *customWorkouts;
NSArray *passedWorkout;
}
#property(nonatomic, retain)NSManagedObjectContext *managedObjectContext;
#property(nonatomic, retain)NSMutableArray *customWorkouts;
#property(nonatomic, retain)NSArray *passedWorkout;
-(IBAction)customWorkouts:(id)sender;
-(void)passWorkoutBack:(NSArray *)workout;
#end
WorkOutList.m
#implementation WorkOutList
#synthesize managedObjectContext, customWorkouts, passedWorkout;
- (void)viewDidLoad {
[self setupContext];
NSLog(#"View Did Load");
customWorkouts = [[NSMutableArray alloc] init];
passedWorkout = [[NSArray alloc] init];
[super viewDidLoad];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self fetchWorkoutList];
NSLog(#"View will Appear");
NSLog(#"Array from View Will Appear : %#", passedWorkout);
}
-(IBAction)customWorkouts:(id)sender{
CoCoachAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
SelectedWorkout *selectedWorkout = [[SelectedWorkout alloc] initWithStyle:UITableViewStyleGrouped];
[selectedWorkout recieveNeededData:customWorkouts];
[appDelegate.practiceNavController pushViewController:selectedWorkout animated:YES];
[selectedWorkout release];
}
-(void)passWorkoutBack:(NSArray *)workout{
passedWorkout = workout;
[passedWorkout retain];
}
- (void)dealloc {
[super dealloc];
}
SelectedWorkout.h
#import <UIKit/UIKit.h>
#interface SelectedWorkout : UITableViewController {
NSMutableArray *workoutListForTable;
}
#property(nonatomic,retain)NSMutableArray *workoutListForTable;
-(void)recieveNeededData:(NSMutableArray *)workoutList;
#end
SelectedWorkout.m(aside from all the stuff to set up the tableView)
#implementation SelectedWorkout
#synthesize workoutListForTable;
-(void)recieveNeededData:(NSMutableArray *)workoutList{
if (workoutListForTable != workoutList) {
workoutListForTable = workoutList;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView deselectRowAtIndexPath:indexPath animated:YES];
CoCoachAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
WorkOutList *workoutListView = [[WorkOutList alloc]init];
[workoutListView passWorkoutBack:[workoutListForTable objectAtIndex:indexPath.row]];
[appDelegate.practiceNavController popViewControllerAnimated:YES];
}
- (void)dealloc {
[workoutListForTable release];
[super dealloc];
}
NSLog(#"other table : %#", workoutListForTable);
[workoutListForTable retain];
}

In this line:
passedWorkout = [[NSArray alloc] init];
You're creating an immutable array with nothing in it. What do you want it to contain?

It looks to me that although you are initializing your array with or so I assume, since on the other line you are only allocating an empty array.
[self fetchWorkoutList]
You are resetting it here every time:
[workoutListView passWorkoutBack:[workoutListForTable objectAtIndex:indexPath.row]];
As a note here:
-(void)passWorkoutBack:(NSArray *)workout{
passedWorkout = workout;
[passedWorkout retain];
}
You have the property passedWorkout as retain already, however you need to call it on self
-(void)passWorkoutBack:(NSArray *)workout{
self.passedWorkout = workout;
}

Related

iphone delegate protocol can't save object

ok, i was trying to understand this post about best way to transfer data from one view controller to other.
the thing is, if i want to set an attr of the object its works like a champ. If i try to set the entire object, it doesnt do it.
my code is:
#protocol AppDelegateProtocol
- (Lote*) theLoteAppObject;
#end
on AppDelegate:
#interface AgroferiaAppDelegate : NSObject <UIApplicationDelegate, AppDelegateProtocol> {
Lote *theLoteAppObject;
}
#property (nonatomic, retain) Lote *theLoteAppObject;
#end
...
...
- (id) init;
{
self.theLoteAppObject = [[Lote alloc] init];
[theLoteAppObject release];
return [super init];
}
the class where i get the problem (UIViewcontroller):
-(void)tableView:(UITableView *) aTableView didSelectRowAtIndexPath:(NSIndexPath *) indexPax{
...
NSArray *lotes = [[self.evento lotesStore]allLotes] ;
Lote* theDataObject = [self theLoteAppObject];
theDataObject._id = [[lotes objectAtIndex:[indexPax row]]_id];
[[self navigationController]pushViewController:lotesOpViewController animated:YES];
}
- (Lote*) theLoteAppObject;{
id<AppDelegateProtocol> theDelegate = (id<AppDelegateProtocol>) [UIApplication sharedApplication].delegate;
Lote* theDataObject;
theDataObject = (Lote*) theDelegate.theLoteAppObject;
return theDataObject;
}
so that works, but if i want to do the followimg,
theDataObject = [lotes objectAtIndex:[indexPax row]];
it does not save the object on theDataObject.
is this a problem of bad memory managment?
edit: is it theDataObject a reference from appDelegate ?? or here is the problem?
try something like this:
if([indexPax row] < [lotes count])
{
Lotes * dataObjectToCopy = [lotes objectAtIndex: [indexPax row]];
if(dataObjectToCopy)
{
theDataObject = [dataObjectToCopy copy];
}
}
This creates a separate, retained copy of your Lote object. Make sure to release it when you're finished with it (if you're not using ARC).

iPhone Application Error Problem

Bear with me on this one.
I have an iphone application. It is a questionnaire application. There are several types of question, some have a slider, some have text input etc. I have developed a view controller for each type of question.
Two example types of question controllers are: TextInputQuestionViewController and SliderQuestionViewController.
I have a rootViewcontroller named QuestionnaireViewController. This is defined as follows:
#import <UIKit/UIKit.h>
#import "JSONKit.h";
#import "dbConnector.h"
#import "SliderQuestionViewController.h";
#import "TextInputQuestionViewController.h";
#import "MainMenuProtocol.h";
#interface QuestionnaireViewController : UIViewController {
NSDictionary* questions;
NSMutableArray* questionArray;
NSMutableArray* answerArray;
dbConnector* db;
SliderQuestionViewController* currQ; //need to create a generic var
TextInputQuestionViewController* currQ;
NSInteger currQNum;
NSString* qaTitle;
NSString* secId;
id<MainMenuProtocol>delegate;
}
#property(nonatomic, retain) NSDictionary* questions;
#property(nonatomic, retain) NSMutableArray* questionArray;
#property(nonatomic, retain) NSMutableArray* answerArray;
#property(nonatomic, retain) dbConnector* db;
#property(nonatomic, retain) SliderQuestionViewController* currQ;
#property(nonatomic, retain) TextInputQuestionViewController* currTI;
#property(nonatomic) NSInteger currQNum;
#property(nonatomic, retain) NSString* qaTitle;
#property(nonatomic, retain) NSString* secId;
#property(nonatomic, retain) id <MainMenuProtocol> delegate;
-(void) setQuestions;
-(void) startQuestion:(NSInteger)index isLast:(BOOL)last;
-(void) loadQuestions;
-(void) initialise;
-(void) finishQuestionnaire:(id)sender;
-(void) switchViews:(id)sender;
#end
#import "QuestionnaireViewController.h"
#import "dbConnector.h"
#import "ASIHTTPRequest.h"
#import "JSONKit.h";
#import "Answer.h";
#implementation QuestionnaireViewController
#synthesize questions, questionArray, db, currQ, currQNum, answerArray, qaTitle, secId, delegate;
-(void)viewDidLoad{
[self initialise];
answerArray = [[NSMutableArray alloc]init];
[super viewDidLoad];
self.title = qaTitle; //set to whatever section is
}
-(void) initialise {
currQNum = 0;
[self loadQuestions];
UIBarButtonItem *anotherButton = [[UIBarButtonItem alloc] initWithTitle:#"Start" style:UIBarButtonItemStylePlain target:self action:#selector(switchViews:)];
self.navigationItem.rightBarButtonItem = anotherButton;
}
-(void) loadQuestions {
db = [[dbConnector alloc]init];
//code to initialise view
[db getQuestions:secId from:#"http://dev.speechlink.co.uk/David/get_questions.php" respondToDelegate:self];
}
//called when questions finished loading
//stores dictionary of questions
- (void)requestFinished:(ASIHTTPRequest *)request
{
NSData *responseData = [request responseData];
NSString *json = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
NSDictionary *qs = [json objectFromJSONString];
self.questions = qs;
[json release];
[qs release];
[self setQuestions];
}
//assigns JSON to question objects
-(void) setQuestions {
questionArray = [[NSMutableArray alloc] init];
for (NSDictionary *q in self.questions) {
/* Create Question object and populate it */
id question;
if([[q objectForKey:#"type"] isEqualToString:#"Slider"]){
question = [[SliderQuestionViewController alloc]init];
//set min max values
}else if([[q objectForKey:#"type"] isEqualToString:#"Option"]){
}else if([[q objectForKey:#"type"] isEqualToString:#"TextInput"]){
question = [[TextInputQuestionViewController alloc]init];
}else if([[q objectForKey:#"type"] isEqualToString:#"ImagePicker"]){
}else{
//comments
}
//if else to create appropriate view controller - NEED to identify question type
[question setQuestionId:[q objectForKey:#"questionId"] withTitle:[q objectForKey:#"question"] number:[q objectForKey:#"questionNumber"] section:[q objectForKey:#"sectionId"] questionType: [q objectForKey:#"type"]];
/* Add it to question (mutable) array */
[questionArray addObject:question];
[question release];
}
}
-(void) startQuestion:(NSInteger)index isLast:(BOOL)last{
//currQ = [[QuestionViewController alloc]init];
currQ = [questionArray objectAtIndex:index];
//push currQ onto navigationcontroller stack
[self.navigationController pushViewController:currQ animated:YES];
[currQ addButton:self isLast: last];
}
//pushes new view onto navigation controller stack
-(void) switchViews:(id)sender{
Answer* ans = currQ.question.answer;
ans.questionId = currQ.question.qId;
ans.entryId = #"1";//temporary;
if(currQNum < [questionArray count] - 1){
if(currQNum > 0){
//if else for different input types
NSString* qt = currQ.question.qType;
if([qt isEqualToString:#"Slider"]){
ans.answer = currQ.sliderLabel.text;
}else if([qt isEqualToString:#"Option"]){
}else if([qt isEqualToString:#"TextInput"]){
//NSLog(#"%#", currQ.inputAnswer);
ans.answer = currQ.inputAnswer.text;
}else if([qt isEqualToString:#"ImagePicker"]){
}else{
}
[answerArray addObject: ans];
[ans release];
}
[self startQuestion:currQNum isLast:FALSE];
currQNum++;
}else{
ans.answer = currQ.sliderLabel.text;
[answerArray addObject: ans];
//store data temporarily - section finished
[self startQuestion:currQNum isLast:TRUE];
currQNum++;
}
[ans release];
}
-(void) finishQuestionnaire:(id)sender{
//go back to main manual
//if else statement for answers
NSString* answ = currQ.sliderLabel.text;
[answerArray addObject: answ];
[delegate finishedSection:answerArray section:secId];
[answ release];
[self.navigationController popToRootViewControllerAnimated:YES];
}
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
self.questions = nil;
self.currQ = nil;
[super viewDidUnload];
}
//hide back button in navigation bar
- (void) viewWillAppear:(BOOL)animated{
self.navigationItem.hidesBackButton = YES;
}
- (void)dealloc {
[currQ release];
[db release];
[questionArray release];
[questions release];
[super dealloc];
}
#end
the problematic lines with the above are in the switchViews function. I need to make the answer equal to the specific input component in that question view (slider value, text input value). So I need to make currQ a type that can be instantiated using any view controller.
I therefore need a generic variable to hold the current question. currQ holds the current question, but at the moment is of type SliderQuestionViewController. I tried to change this to id, but it throws a load of "Request For member...not a structure of union" and also a load of misassigned pointer issues.
Let me know if you need more code.
This reads like you want a pointer for a UIViewController, so just use that as the type. Then you can cast it down to whatever subclass you like later. For example:
-(void)myAction:(UIViewController *)vc {
SpecialViewController *svc = (SpecialViewController *)vc;
...
}
In your case, declare
UIViewController* currQ;
and then cast it as needed in the implementation to access the different properties and methods of your two subclasses.
If you're looking for a 'generic' variable, you should be able to use id. Make sure you don't define the type as id*, the asterisk should not be present.
A better idea, though, is to create a superclass for your question viewcontrollers. Create a superclass called QuestionViewController that inherits from UIViewController and have the Slider and TextInput (and any others) inherit from QuestionViewController. Then you can define your variable as: QuestionViewController* currQ; You can put any common functionality in that superclass as well and eliminate duplication.

Accessing variables from another ViewController

FirstViewController.h:
#import <UIKit/UIKit.h>
#interface FirstViewController : UIViewController
{
NSArray *listData;
}
-(IBAction) GoToInsert: (id) sender;
#property (nonatomic, retain) NSArray *listData;
#end
FirstViewController.m:
-(IBAction) upisiRezultat:(id)sender
{
SecondViewController *secondView = [[SecondViewController alloc] initWithNibName: nil bundle: nil];
[self presentModalViewController: secondView animated: NO];
[secondView release];
}
- (void)viewDidLoad
{NSArray *array = [[NSArray alloc] initWithObjects:#"236", #"46",
#"147", #"8", #"56", #"69", #"114", #"2",
#"96", #"518", #"2", #"54", #"236", nil];
self.listData = array;
[array release];
[super viewDidLoad];
}
SecondViewontroller.h
#interface SecondViewController : UIViewController {
}
-(IBAction) insert;
#end
SecondViewontroller.m
-(IBAction) insert
{
/* Here should be the code to insert some number in listData from FirstViewController */
}
So when the app loads it loads FirstViewController.xib and shows the listData array on screen, when I click button "Go to Insert" another view is loaded (SecondViewController.xib) with button "Insert" which should add some number into the array and display the new array on the first view.
How to do that?
You can access the parent view controller with self.parentViewController. Therefore something along these lines (meaning I haven't tested this -- you should) should work:
FirstViewController *firstViewController = self.parentViewController;
NSMutableArray *newArray = [[NSMutableArray alloc] initWithArray:firstViewControl.listData];
[newArray addObject:#"42"];
[firstViewController setListData:[NSArray arrayWithArray:newArray]];
[newArray release];
However, since you want to add objects to listArray, it would be more natural to use an NSMutableArray instead. Also, you are currently adding NSStrings to the array, when it looks more like you want to have NSNumber objects.
Alternately, and maybe easier, you could have the variables in your main AppDelegate.
Put:
int myNumber;
in the projectname_AppDelegate.h file
Then in each of your view controllers, you can import your AppDelegate.h file and then do something like:
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
and then read or change:
appDelegate.myNumber
===
This is not something you should be doing all the time (you don't want your appDelegate to be a giant data repository) but it could give you a quick fix when needed...
I do not know if you have imported the SecondViewController.h , and I think I have an idea an what you are trying to do.

dimissModalViewControllerAnimated causing tableView to be full screen?

I have a RootViewController with 2 tableViews as subviews (created in IB) each with their own tableViewController class (handleing fetchRequests etc.)
1 tableView is static (no data changed by user or modelViews).
tableView 2 has a button in the header which presents an imagePickerController.
No issues so far.
Problem is, when i dismiss the imagePicker
[self dismissModalViewControllerAnimated:YES];
TableView 2 becomes full screen i have tried
[[self rootViewController] dismissModalViewControllerAnimated:YES]
Nothing happens at all. It sticks on the image picker.
I suspect this is due to there being very little of the view being created programmaticaly.
Any ideas?
Thanks in advance.
DetartrateD
-(IBAction)addImageTableAPressed {
UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];
imagePicker.delegate = self;
[self presentModalViewController:imagePicker animated:YES];
[imagePicker release];
}
RootViewController
|| ||
|| ||
\/ \/ addImageTableAPressed
TableViewControlA TableViewControlB --------------------->modelViewController
To resolve mananagedObjectContect.....
- (void)viewDidLoad {...
if(managedObjectContext == nil)
{
managedObjectContext = [(AppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
NSLog(#"After managedObjectContext: %#", managedObjectContext);
}
...
}
As I mentioned in one of my comments, I would prefer having a single view controller managing the two table views. Define a UIView (the rootView) including 2 subviews (tableViewA and tableViewB). Your RootViewController's view will be rootView, and this controller will have to be the data source and delegate of both table views. The code I will give here is by no means complete nor optimal, but gives you a good idea of what is needed to implement my solution.
For example:
#interface RootViewController <UITableViewDelegate, UITableViewDataSource> {
NSArray *dataArrayA;
NSArray *dataArrayB;
UITableView tableViewA;
UITableView tableViewB;
NSManagedObjectContext *context;
}
#property (nonatomic, retain) NSArray *dataArrayA;
#property (nonatomic, retain) NSArray *dataArrayB;
// in IB, link the dataSource and delegate outlets of both tables to RootViewController
#property (nonatomic, retain) IBOutlet UITableView tableViewA;
#property (nonatomic, retain) IBOutlet UITableView tableViewB;
// this property will allow you to pass the MOC to the RootViewController from
// the parent view controller, instead of accessing the app delegate from RootViewController
#property (nonatomic, retain) NSManagedObjectContext *context;
// ... etc.
#end
#implementation RootViewController
#synthesize dataArrayA;
#synthesize dataArrayB;
#synthesize tableViewA;
#synthesize tableViewB;
#synthesize context;
// initialize dataArrayA and dataArrayB
- (void)viewDidLoad {
[super viewDidLoad];
NSError *error = nil;
// initialize and configure your fetch request for data going into tableViewA
NSFetchRequest fetchRequestA = [[NSFetchRequest alloc] init];
// configure the entity, sort descriptors, predicate, etc.
// ...
// perform the fetch
self.dataArrayA = [context executeFetchRequest:fetchRequestA error:&error];
// do the same for the data going into tableViewB - the code is very similar, you
// could factor it out in a private method instead of duplicating it here
// NSFetchRequest fetchRequestB = [[NSFetchRequest alloc] init];
// omitting the details ... etc.
self.dataArrayB = [context executeFetchRequest:fetchRequestB error:&error];
// release objects you don't need anymore, according to memory management rules
[fetchRequestA release];
[fetchRequestB release];
}
// Table view methods
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// if you have a different number of sections in tableViewA and tableViewB
/*
if (tableView == tableViewA) {
return ??;
} else {
return ??
}
*/
// otherwise, if both table views contain one section
return 1;
}
// Customize the number of rows in each table view.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (tableView == tableViewA) {
return [dataArrayA count];
} else {
return [dataArrayB count];
}
}
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = nil;
if (tableView == tableViewA) {
// get the data for the current row in tableViewA
id objectA = [dataArrayA objectAtIndex:indexPath.row];
// configure the cell for tableViewA
cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifierA];
// etc...
} else {
// get the data for the current row in tableViewB
id objectB = [dataArrayB objectAtIndex:indexPath.row];
// configure the cell for tableViewB
cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifierB];
// etc...
}
return cell;
}
// And so on, the same idea applies for the other UITableViewDelegate you would need to
// implement...
- (void)dealloc {
[dataArrayA release];
[dataArrayB release];
[tableViewA release];
[tableViewB release];
[context release];
// etc...
[super dealloc];
}
#end
I hope you'll find this useful.

NSIndexPath: message sent to deallocated instance

Any help you can offer would be appreciated.
I have a app that has a tableView, detailView, flipView, and moreDetail View. It is fine moving through the transitions until the 26th element. The app crashes when I hit buttons that navigate to the flip and moreDetailView.
I am given the error Msg: [NSIndexPath row] message sent to deallocated instance.
I think I may be calling the indexPath too many times, but why does everything work well up until the 25th element and then stop working? How did the NSIndexPath get deallocated? I never deallocated it.
If you know, please help. Thank you!!!
Xcode says the problem is here:
#implementation produceView aka *detailView*
- (IBAction)showInfo {
FlippedProduceView *fView = [[FlippedProduceView alloc]initWithIndexPath:index];
fView.flipDelegate = self;
fView.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:fView animated:YES];
[fView release];
}
- (IBAction) buttonPushed:(id)sender
{
picView *pictureView = [[picView alloc]initWithIndexPath:index];
pictureView.picDelegate = self;
pictureView.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
[self presentModalViewController:pictureView animated:YES];
[pictureView release];
}
-(id)initWithIndexPath:(NSIndexPath *)myIndexPath {
if (self == [super init]) {
path = myIndexPath;
}
return self;
}
#implementation picView aka *moreDetailView*
- (void)viewDidLoad {
RipeGuideAppDelegate *AppDelegate = (RipeGuideAppDelegate *)[[UIApplication sharedApplication] delegate];
self.picViewArray = AppDelegate.arrayProduceInfo;
NSMutableDictionary *dictionary = [picViewArray objectAtIndex:path.row];
}
#implementation FlippedProduceView aka *flipView*
- (id)initWithIndexPath:(NSIndexPath *)myIndexPath {
if (self == [super init]) {
indexes = myIndexPath;
}
return self;
}
- (void)viewDidLoad {
RipeGuideAppDelegate *AppDelegate = (RipeGuideAppDelegate *)[[UIApplication sharedApplication] delegate];
self.flipViewArray = AppDelegate.arrayProduceInfo;
NSMutableDictionary *dictionary = [flipViewArray objectAtIndex:indexes.row];
}
You should use properties (#property (retain) NSIndexPath *path;) instead of instance variables, so then when you do self.path = myIndexPath then it raises the retain count so it won't be deallocated when IndexPath is autoreleased.