How to share state between viewControllers using the AppDelegate object - iphone

I'm trying to share an NSArray object between several different view controllers and my first thought was to add a property on the app delegate as they all have access to this object.
But after some debugging it appears I can't actually share this array for some reason. When I set the object from the first view controller and NSLog the results all is well. But when I attempt to get that array value using another view controller object it always returns UITouchData (not the value previously shown in the logs after my first view controller set the value)
Here is the code that I'm using to set the value
NSArray* cookies = [NSHTTPCookie
cookiesWithResponseHeaderFields:[response allHeaderFields]
forURL:[NSURL URLWithString:#""]];
[appDelegate setAuthCookie:cookies];
Here is part of the .h for my app delegate
#interface SomeAppDelegate : NSObject <UIApplicationDelegate> {
NSArray* authCookie;
}
#property (retain) NSArray* authCookie;
- (void)setAuthCookie:(NSArray *)cookie;
- (NSArray *)getAuthCookie;
#end
Here is the .m for the methods in question
#synthesize authCookie;
- (void)setAuthCookie:(NSArray *)cookie
{
authCookie = cookie;
}
- (NSArray *)getAuthCookie
{
return authCookie;
}
Here is the attempt to grab this array in the second view controller that fails (technically it doesn't fail on this line but I don't get an NSArray back as expected so when I try to use this it fails)
NSArray* cookies = [appDelegate getAuthCookie];
Any way I can share state using the app delegate like this?

Your memory management is wrong, and you are getting a completely different object which has inherited the old array's address when you later use the getter.
Your #property is correct, but you've written your own setter and getter that do not retain the object. You don't need to use both #property/#synthesize and supply your own getter/setter. The former is a newer means of automating the latter.
If you remove your implementations of setAuthCookier: and getAuthCookie then your code should work.

Related

NSManagedObjectContext executeFetchRequest returns array containing objects that don't stick around

I have a UITableView I'm populating with data from CoreData. I have a data access class I call a method on to get the array of data to populate the table with. In that method I have something like this:
NSArray *fetchedArray = [context executeFetchRequest:request error:&error];
I was just returning this array directly, but was getting an error in my view controller when I try to set its local property that holds the returned array
#property (nonatomic, retain) NSArray *listData;
and
#synthesize listData; // <-- error here -[CFNumber release]: message sent to deallocated instance...
respectively.
It seemed like the 'retain' in my #property was trying to release the previous listData array, which seemed to have already been released (or, more likely an object in the array or one of its properties had been released).
So in my data access class I added the following after the fetchedArray is produced by the context:
NSMutableArray *listArray = [[[NSMutableArray alloc] init] autorelease];
for (Response *item in fetchedArray) {
[listArray addObject:item];
}
return listArray;
But I still get the same error in the #synthesize listData back in the view controller. It doesn't happen the first time usually, but after tapping through to the detail controller and then going back to the list and then reloading the list with different data (e.g. filtering based on user input which calls the data access method to return an updated list - hence the error in the setter for listData).
I'm not entirely sure if my problem is memory management related or related to something I'm not understanding about what the context returns. It'd be nice if a fetch request returned data that didn't get released when I think I've retained it. :(
EDIT Note that given the answer, the title of the question may be a bit misleading.
Ah - just had to think it through a bit more. My problem was that I was assigning one of the objects in the array to a property on the detail controller, but calling release on that property in my detail controller's dealloc.
After changing #property (nonatomic, assign) to #property (nonatomic, retain) it doesn't crash. Yay. Sooo looking forward to ARC.

EXC_BAD_ACCESS accessing an NSArray on Delegate app

I am developing an iPhone app and I have a problem accessing to one NSArray defined on delegate.
My app has two tabs. These two tabs have an UITableView. I have the source of the data of these tables on one NSArray defined in the delegate.
When I load the application, the first tab loads correctly all the content. This is one part of code of the first tab controller:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in the section.
mainAppDelegate *delegate = [[UIApplication sharedApplication]delegate];
return [delegate.dataArray count];
}
When the debugger is on return statement, I can see that dataArray has 9 elements. The code works fine.
When I click on the second tab, the same code is executed in the second's tab controller. But while debugger is on return statement, I can see that delegate is not null and dataArray shows
{(int)[$VAR count]} objects
On next step, program crashes giving EXC_BAD_ACCESS
I think in this point dataArray has been released, but I don't know when. I have deleted dataArray release calls on dealloc functions.
This is the property definition of dataArray:
#property (nonatomic, retain) NSArray *dataArray;
Anyone has any idea? I'm completely lost on it. Thanks for your help!
As per your comment, you initialize your array like this:
dataArray = [dict objectForKey:#"data"];
This does not retain the array since you are accessing the ivar directly, not the property. You should do this instead:
self.dataArray = [dict objectForKey:#"data"];
Some programmers prefer to synthesize their properties with a different ivar name to avoid such mistakes.
#synthesize dataArray = dataArray_;
In your comments you just said that dataArray is the response of a servlet and you're parsing it with json-framework.
This sounds like your dataArray object just stores a reference to an external array object being passed in.
You haven't posted much code. Could you show where your dict object is initialized?
Your external object might be getting released causing an EXC_BAD_ACCESS

Accessing Class Data Members in ViewController.m (xCode / Objective C)

I have a class myClass and would like to access its properties, a NSArray *currentOptions (specifically to get the size of currentOptions and access the NSStrings which I've put in it.)
I have a method called generate options which assigns an filled array to *currentOptions. Generate options is called before I try to access *currentOptions. An instance of myClass has also been added to the ViewController via the App delegate. However when buttonOnePressed is called, I keep getting this error:
[myClass currentOptions]: unrecognized selector sent to instance 0x9b10490
Here is the parts of my code:
//TClass.h
#interface TClass : NSObject {
NSArray *currentOptions;
}
#property (nonatomic, retain) NSArray *currentOptions;
#end
//viewController
- (IBAction) buttonOnePressed:(id)sender {
NSLog(#"button1 pressed");
NSLog(#"int: %d",[myClass.currentOptions count]);
//myClass here is the instance of TClass
}
One thing that sometimes causes that error is failing to properly retain myClass. (Aside: "myClass" is a really bad name for a pointer because the thing being pointed to is almost certainly not a class but an object, i.e. an instance of a class.) If you don't retain the object that myClass points to, it will be deallocated. Sometimes, a different object happens to be created at that some location, and you end up sending a message meant for the original object to the new one, which is a different type and doesn't understand the message.
To all who have been following, the problem has been resolved by making the following changes:
1) Synthesized current options TClass.m
#implementation TClass
#synthesize currentOptions;
#end
2) I made currentOptions a NSMutableArray instead of a NSArray. This is because I need to reassign values to current options. Somehow it crashes with NSArray and everything goes smoothly with NSMutable array like such
#implementation TutorialClass
if ([currentOptions count] > 0) {
[currentOptions removeAllObjects];
}
[currentOptions addObject:[options objectAtIndex:0]];
[currentOptions addObject:[options objectAtIndex:1]];
[currentOptions addObject:[options objectAtIndex:2]];
[currentOptions addObject:[options objectAtIndex:3]];
3) And of course, I'll also have to do the following in the init method of TClass.m
currentOptions = [[NSMutableArray alloc] init];
Now its time to get some food. Thanks Caleb :D

AppDelegate: Get value from ViewController?

I would like to get at
- (void)applicationWillTerminate:(UIApplication *)application
a variable from a view controller class.
I have build an tabbar application and only added the tabbar controller to the appdelegate.
[window addSubview:tabBarController.view];
How can i get an variable from the TestViewController:
#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
#interface TestViewController : UIViewController {
IBOutlet UILabel *testLabel;
NSString *currentString; //Value that i want to save at applicationWillTerminate
}
#property (nonatomic, retain) UILabel* testLabel;
#property (nonatomic, retain) NSString* currentString;
#end
It's somewhat incidental that TestViewController hasn't been dealloc'd by the time you reach applicationWillTerminate - it might make sense to store that value a level higher in your application. That approach would be to always store currentString in the UIApplicationDelegate so that you don't have to fetch it later:
#implementation TestViewController
- (void)setCurrentString:(NSString *)currentString {
((MyAppDelegate *)[[UIApplication sharedApplication] delegate]).currentString = currentString;
}
#end
Expanding on dbarker's answer, it sounds like what you really need is to save the currentString value in your data model. The proper place to do that is in the viewController itself.
If your data model is just that one string, you can create a property in the app delegate to hold it. Then the viewController writes to the app delegate property whenever the value of currentString changes in a view and/or its value when the view closes.
This way, the data (which is the entire point of the app anyway) is always in place when the app closes regardless of how many views you open.
It is the proper role of controllers to move information from the interface to the data model. Strictly speaking, the viewController shouldn't store any data at all beyond that needed by the interface itself. That should be a property of the data model which the viewControllers set by sending a message to the data model object with the values taken from the interface.
In this case, you would not have a currentString property in your view controllers. Instead they would have a property that is just a reference to the data model's currentString property. The view controllers would continuously update that property but would store nothing themselves.
The advantage of this design is obvious. If you need the value anywhere in your app, you have one location and one call to get it. No single part of the app needs to even know of the existence of any other part of the app save for the data model.
Not 100% sure what you are asking for but here is a guess:
UITabBarController's have a property called viewControllers which return all view controllers associated with the tabbar.
Assuming that the TestViewController was the first tab you could get to it by:
- (void)applicationWillTerminate:(UIApplication *)application {
TestViewController* test_view_controller = [tabBarController.viewControllers objectAtIndex:0] ;
NSString* value = test_view_controller.currentString ;
}
Note this would break if you decided to later move the TestViewController to a different position in the tabbar.
-- Edit --
Check all controllers and get the string from the controller that is of type TestViewController.
NSString* value = nil ;
for ( id unknownController in tabBarController.viewControllers ) {
if ( [unknownController isKindOfClass:[TestViewController class]] ) {
value = ((TestViewController*)unknownController).currentString ;
}
}
// value should be the value of the string.

Passing a Property to a function and setting the value to be retained

I have two properties setup as ViewControllers that each use different NIB file. (Male and Female Models, will function the same but are setup visually different.) I want to have one function to create the ViewController based on the NIB Name and ViewController I pass in.
What's happening is the ViewController property is not being retained. If I add the actual property name within the function and set it, the viewController is set and retains the ViewController. Here is what I have in the .m file (Only showing what is needed to get help on.) I've kept in the two comment lines that I tried to do determine where the problem was.
#synthesize femaleModelViewController;
#synthesize maleModelViewController;
- (void) loadModelViewControllerWithModelType:(NSString*) model ModelView:(ModelViewController *)modelViewController {
ModelViewController *viewController = [[ModelViewController alloc] initWithNibName:model bundle:nil];
// [self setFemaleModelViewController:viewController]; // I don't want to set the property here, I want to be able to pass it as an argument.
modelViewController = viewController;
// [modelViewController retain]; // I even tried to retain it do see if would but it doesn't.
[viewController release];
}
- (void)viewDidLoad {
[super viewDidLoad];
[self loadModelViewControllerWithModelType:#"FemaleModel" ModelView:femaleModelViewController];
[self loadModelViewControllerWithModelType:#"MaleModel" ModelView:maleModelViewController];
}
In the .h file my Properties are setup like so:
#property (nonatomic, retain) ModelViewController *femaleModelViewController;
#property (nonatomic, retain) ModelViewController *maleModelViewController;
Are you trying to assign a new ModelViewController to maleModelViewController and femaleModelViewController? If so, you're going about it the wrong way.
Frankly, I'd do away with the loadModelViewControllerWithModelType:modelView: method altogether. All you would need to do in viewDidLoad is this:
maleModelViewController = [[ModelViewController alloc] initWithNibName:#"MaleModel"];
femaleModelViewController = [[ModelViewController alloc] initWithNibName:#"FemaleModel"];
(This assumes that maleModelViewController and femaleModelViewController are the instance variables backing the properties of the same name.)
When you pass in maleModelViewController and femaleModelViewController, you're not passing a reference to those variables, you're passing their values. Since they haven't been initialized, all you're doing is passing in nil. To do what you're trying to do, you'd need to pass a pointer to the variables (i.e. declare the parameter as ModelViewController **, pass it in as &maleModelViewController and &femaleModelViewController and assign it using *modelViewController = viewController. If you're used to using pass-by-reference friendly languages like C# or Java, you should read up on how it works in C. (The rules are the same in Objective-C as in C).
HOWEVER, that is still complete overkill for what you're trying to do here.
Also, your code would still not work because you turn around and release viewController. This would cause it to be immediately deallocated, since the only reference you had to it came from when you alloced it. You would be assigning a dead reference that would crash your program as soon as you tried to use it.