iPhone: Using a NSMutableArry in the AppDelegate as a Global Variable - iphone

What i'm trying to accomplish is to have an NSMutableArray defined in the AppDelegate. I then have two UIViewControllers. One view is responsible for displaying the array from the AppDelegate. The other view is used to add items to the array. So the array starts out to be empty. View1 doesn't display anything because the array is empty. The User goes to View2 and adds an item to the array in AppDelegate. Then when the user goes back to View1 it now displays one item.
Here is how I'm trying to accomplish this
#interface CalcAppDelegate : NSObject <UIApplicationDelegate> {
UIWindow *window;
UITabBarController *tabBarController;
NSMutableArray *globalClasses;
}
#property (nonatomic,retain) NSMutableArray *globalClasses;
My other view
In the viewDidload I set the array in my View to be the one in the AppDelegate. In an effort to retain values.
allCourses = [[NSMutableArray alloc]init];
CalcAppDelegate *appDelegate = (CalcAppDelegate *)[[UIApplication sharedApplication] delegate];
allCourses = appDelegate.globalClasses;
Then I would update my allCourses array by adding a new item. Then try to set the array in the AppDelegate to be equal to the modified one.
CalcAppDelegate *appDel = (CalcAppDelegate *)[[UIApplication sharedApplication] delegate];
NSLog(#"Size before reset %d",[appDel.globalClasses count]);
appDel.globalClasses = allCourses;
NSLog(#"Size after reset %d",[appDel.globalClasses count]);
What I'm seeing that's returned is 2 in the before, and 2 after. So it doesn't appear to be getting updated properly. Any suggestions?

A few things:
First, in your app delegate you need to make sure that you intialize the array before any object tries to access it. A customer getter is good for this.
-(void) getGlobalClasses{
if (globalClasses!=nil) {
return globalClasses;
}
NSMutableArray *newArray=[[NSMutableArray alloc] initWithCapacity:1]; //yes, I'm old school
self.globalClasses=newArray;
[newArray release];
return globalClasses;
}
Now any call to the property is guaranteed to return an array.
In your view controllers, you need to define properties to hold the array. Since the array is held by the app delegate and will always be there, it is best to assign the array instead of retaining it. That way you always know you are writing to the exact same array and the app delegate has complete control over its life cycle.
In the view controllers:
#property(nonatomic,assign) NSMutableArray *globalClasses;
then every time you reference it make sure to use the self notation:
self.globalClasses=//...whatever
Having said all this, it is extremely bad practice to stick an array or any other dumb data object out their buck naked in your app. You have no control over what each piece of code will do to the array. You will have to duplicate all your validation code every place you add or remove data to the array.
It would be better to wrap the array in a custom class and make it protected so it can only be altered by the classes methods.
Like so:
#interface MyData : NSObject {
#protected
NSMutableArray *myDataArray;
}
-(void) addObject:(id) anObject;
-(void) removeObjectAtIndex;(NSInteger) anIndex;
#end
scratch.m
#interface scratch ()
#property(nonatomic, retain) NSMutableArray *myDataArray;
#end
#implementation scratch
#synthesize myDataArray;
-(void) addObject:(id) anObject{
//...code to check if anObject is a valid one to add to the array
[self.myDataArray addObject:anObject];
}//------------------------------------addObject:------------------------------------
-(void) removeObjectAtIndex;(NSInteger) anIndex{
//... do bounds checking and other testing to ensure no problems
// will result from removing the object at the given idex
[self.myDataArray removeObjectAtIndex:anIndex];
}//-------------------------------------(void) removeObjectAtIndex;(NSInteger) anIndex------------------------------------
Then add the custom class an a property of the app delegate as shown above. This will keep your data clean and modular so you can safely use it in a wide range of app reobjects without having to micromanage the array in every object.

There are a couple problems here:
allCourses = [[NSMutableArray alloc] init];
CalcAppDelegate *appDelegate = (CalcAppDelegate *)[[UIApplication sharedApplication] delegate];
allCourses = appDelegate.globalClasses;
You do not need to allocate a new array since you want to retain an existing array (the one in the app delegate)
If you're using properties, you need to use the self declaration to retain the app delegate array
Instead, try:
CalcAppDelegate *appDelegate = (CalcAppDelegate *)[[UIApplication sharedApplication] delegate];
self.allCourses = appDelegate.globalClasses;

Related

Can i have a single NSMutableArray in my multiple views application?

I have a navigational based application which has multiple views. Is it possible to use one single NSMutableArray for the whole applicaiton? Can i add objects to that NSMutableArray in one view and then remove object from the same NSMutableArray from some other view? I tried
myappAppDelegate *appDelegate = (myappAppDelegate *)[[UIApplication sharedApplication] delegate];
but it gives me null when i try to access appDelegate's array. If anyone can give me any idea or helping link or tutrorial. Thanks in advance.
If you are having multiple views in your application, and in that case you want to have a variable accessible to every view, you should always create a Model/Data(singleton) class and define the variable in it. Something like this :
//DataClass.h
#interface DataClass : NSObject {
NSMutableArray *arrGlobal;
}
#property(nonatomic,retain)NSMutableArray *arrGlobal;
+(DataClass*)getInstance;
#end
//DataClass.m
#implementation DataClass
#synthesize arrGlobal;
static DataClass *instance =nil;
+(DataClass *)getInstance
{
#synchronized(self)
{
if(instance==nil)
{
instance= [DataClass new];
}
}
return instance;
}
Now in your view controller you need to call this method as :
DataClass *obj=[DataClass getInstance];
obj.arrGlobal = arrLocal;
This variable will be accessible to every view controller. You just have to create an instance of Data class.
For your type of issue I would use a singleton.
http://en.wikipedia.org/wiki/Singleton_pattern
The appdelegate is a singleton too but you can reduce a bit the number of coded lines if you use your own singleton.
The AppDelegate approach should work, and you should probably figure out why it's not working, even if you go with a singleton.
The statement to get your appDelegate pointer appears to be correct, so I'm guessing that the pointer to the array is either not getting set (and retained) in your myappDelegate class, or you did not create the AppDelegate instance correctly in the first place.
On the Singleton approach add this
instance.arrGlobal = [[NSMutableArray alloc] init];
this way:
#synchronized(self)
{
if(instance==nil)
{
instance= [DataClass new];
instance.arrGlobal = [[NSMutableArray alloc] init];
}
}
return instance;
This way you can initilize the array and use it properly.

sharing Data between a UIViewController and a UIViewController in UITabBarController xcode

I know how to do for share data between 2 views. But if I want to share data using a tabBarController I'm lost.
This is my IBAction to move to my tabBar.
-(IBAction)goToPage2:(id)sender
{
tabController.modalTransitionStyle=UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:tabController animated:YES];
}
i need to share my NSString *dataStr in my IBAction in the first view of my tabBar.
firstView *first = [[firstView alloc] initWithNibName:#"firstView" bundle:nil];
first.dataStr = name.text;
[tabController presentModalViewController:first animated:YES];
this code doesn't work.
thx
Declare a #property in your app delegate. And you can access your app delegate from any point of your app.
MyAppDelegate *appDelegate = (MyAppDelegate *)[[UIApplication sharedApplication ]delegate]
I agree with what Terente suggested. But I recommend you should use a Data Class in this case. Using a property in Appdelegate is not a good practice. You should always use Data Class for this.
You create a Data class as follows:
You need to create a Data class where you can set the properties of variables or in your case arrays (for displaying data in UITableView). Implement a class method in data class which checks that object has been instantiated or not. If not, it does that. It is something like this :
//DataClass.h
#interface DataClass : NSObject {
NSMutableArray *nameArray;
NSMutableArray *placeArray;
}
#property(nonatomic,retain)NSMutableArray *nameArray;
#property(nonatomic,retain)NSMutableArray *placeArray;
+(DataClass*)getInstance;
#end
//DataClass.m
#implementation DataClass
#synthesize nameArray;
#synthesize placeArray;
static DataClass *instance =nil;
+(DataClass *)getInstance
{
#synchronized(self)
{
if(instance==nil)
{
instance= [DataClass new];
}
}
return instance;
}
Now in your view controller you need to call this method as :
DataClass *obj=[DataClass getInstance];
And use the arrays.
This way you can assign data without disturbing AppDelegate, which is a good practice.
I wrote a lengthy tutorial about such issues on my blog: http://www.hollance.com/2011/04/making-your-classes-talk-to-each-other-part-1/
You need to figure out a clean way to let your controllers communicate with each other. My tutorial explains several ways to do this and what the advantages and downsides are of each approach. It's worth learning how to do this, because this issue comes up in almost any app you will write.

Access to variables through delegate

I've go a situatiion in Objective-C where I'm trying to access an object's variable through another object. The classes (simplified):
A.h
#interface A : NSObject {
NSMutableArray *someStuff;
}
#property (nonatomic, retain) NSMutableArray *someStuff;
#end
A.m
#implementation A
#synthesize someStuff;
// blah, blah, blah
Then, because I'm doing an iPhone app, there is an app delegate that contains a variable of this object type:
AppDelegate.h
#interface AppDelegate : NSObject <UIApplicationDelegate> {
A *aPtr;
}
#property (nonatomic, retain) A *aPtr;
#end
AppDelegate.m
#implementation AppDelegate
#synthesize aPtr;
// blah, blah, blah
Then, in another class (in this a view controller), I'm trying to access 'someStuff' in this manner:
AViewController.m
AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
NSMutableArray *someArray = appDelegate.aPtr.someStuff;
So, the problem is that this blows up in fine fashion. I think I'm too much of a Java junkie to understand why this won't work. Can anyone elighten me?
Many thanks,
Craig
You need to initialize this in this way
AppDelegate *appDelegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];
NSMutableArray *someArray = appDelegate.someArray;
This will resolve your problem.....
Craig,
appDelegate.aPtr will return null. as it is not initialized yet, and when you try to access some member of a null object,(in java NULLPointerException).Its behavior is as expected.(this blows up in fine fashion).
Thanks,
Ravin is correct. The class definition defines the iVars and properties for a class so you have defined an iVar aPtr that references an object of type A. However, you have not allocated and initialized this object.
An example using the default initialization would be `aPtr = [[A alloc] init]'.
This all sounds suspicious since in ObjC you are allowed to send messages to nil without a problem and properties are just diguised methods. For example you can
view = nil;
view.hidden = NO;
and it doesn't blow up, it just does nothing.
So since appDelegate.aPtr.someStuff is just
[[appDelegate aPtr] someStuff];
and [appDelegate aPtr] does nothing and returns nil so it should be safe to call [[appDelegate aPtr] someStuff] without a problem but also without any results.
So while it is a problem with using objects that hadn't been initialized (which most often should be done in a designated constructor of the appropriate object), since you don't get results that you expect, in my undestanding of "sending message to nil" in ObjC it shouldn't blow up. If it is then either I am missing the point or something other causes the problem and not this call.
EDIT
just checked: if not initialized at all it works as I explained: ObjC allows messages to be sent to nil:
A *aPtr = appDelegate.aPtr;
NSMutableArray *someArray = aPtr.someStuff;
NSLog(#"%#", someArray);
or
NSMutableArray *someArray = appDelegate.aPtr.someStuff;
NSLog(#"%#", someArray);
both don't break and print null.
If you initialize A properly but not initialize someStuff in A it still doesn't break but print null. The problem might be that you initialize aPtr to a different class than A, in which case you get unrecognized selector exception (you should be able to see it in the error log) and program crash.

Passing contents of array from one class to another

I've got an array populating a small tableView in a DetailView class, and when the user presses a button I need the array to be sent to another View Controller, to populate a tableView there, but I'm having some difficulty getting it working. This is what I've been trying to do so far:
*DetailViewController.m*
#import "DetailViewController.h"
#import "OtherViewController.h"
-(IBAction) toCart:(id)sender {
OtherViewController *oVC = [[OtherViewController alloc] init];
oVC.shoppingList = sList;
NSLog(#"Ingredients count %d", [sList count]); //This returns a number, so the sList definitely contains values, and the method is definitely being called.
[oVC release];
}
*OtherViewController.m*
#import "OtherViewController.h"
#import "DetailViewController.h"
#synthesize shoppingList;
-(void) viewWillAppear: (BOOL)animated {
NSLog(#"list count: %d", [shoppingList count]); // This returns 0
}
sList is populated elsewhere in the class, and sList and shoppingList are both declared in their respective .h files, with #property (nonatomic, retain)...
Any help much appreciated!
As you are having taBbarcontroller, so you can proceed as follows :
Create references of your you viewControllers(which are associated with tabbar as topViewController) in your appDelegate.
otherViewController = [[tabBarController.viewControllers objectAtIndex:<tabIndex>] topViewController];
make it as #property in appDelegate so that you can access it anywhere in your app.
now,
-(IBAction) toCart:(id)sender {
//appDelegate <--- get reference to your application delegate using [[UIApplication sharedApplicaiton]delegate] do not forget to properly type cast it.
OtherViewController *oVC = [appDelegate otherViewController];
oVC.shoppingList = sList;
NSLog(#"Ingredients count %d", [sList count]);
//This returns a number, so the sList definitely contains values, and the method is definitely being called.
// [oVC release]; no need to release it...
}
//also make sure you do not initialize shoppingList of otherViewController in viewDidLoad(or any other method) of otherViewController, else it will be overwritten(lost its previous reference).
in your appDelegate's .h write
#property OtherViewController *otherViewController;
in appDelegate's.m
#synthesize otherViewController;
in appDelegates's .m (method didFinishLaunchingWithOptions: ) write
otherViewController = [[tabBarController.viewControllers objectAtIndex:<tabIndex>] topViewController];
Thanks
In toCart:, you are creating an OtherViewController and then immediately throwing it away. Whatever OtherViewController is calling -viewWillAppear, it isn't the one you're creating in toCart:. How is that object created and put on the screen? You need a pointer to it to modify it.
Better, though, would be to move your model data out of the view controllers and put it in a single ShoppingCart object. Then all your view controllers would have a reference to it (or you can make ShoppingCart a singleton if that makes sense in your program). This way, any time you change the shopping cart from anywhere, all views will correctly update without having to tell every view controller about every other view controller.

iPhone - UINavigationController question

I want to display some data on table, let's call it "TabeView" app.
So I created a "navigation-based" application on XCode and it gives me 4 files in the "classes folder".
RootViewController.h
RootViewController.m
TableViewAppDelegate.h
TableViewAppDelegate.m
Now, I wanted to set up the data in TableViewAppDelegate using the "didFinishLaunchingWithOptions" method, which I did using an array.
Then, I need to "send" this array to the RootViewController. I have an array variable in RootViewController so I assume that I need to "set" the array variable in RootViewController from the TableViewAppDelegate. How can I do this?
The problem I am having is, that I don't know how to set the array variable in RootViewController from TableViewAppDelegate. I want to know if something like below is possible
....
[RootViewController setMyArray:myArray];
[window addSubview:navigationController.view];
[window makeKeyAndVisible];
....
But I have no idea how to call "RootViewController".
Hopefully I made some sense. Thank you.
First, create a property in your RootViewController class for the array variable. So e.g. if your array variable is called myArray your code might look something like this:
In RootViewController.h:
#import <UIKit/UIKit.h>
#interface RootViewController : UITableViewController {
NSArray *myArray;
}
#property (nonatomic, retain) NSArray *myArray;
#end
and add the corresponding #synthesize line in RootViewController.m:
#import "RootViewController.h"
#implementation RootViewController
#synthesize myArray;
// methods here
#end
Now you can set the myArray member of a RootViewController object like this:
myRootViewController.myArray = [NSArray arrayWithObjects:#"foo", #"bar", nil];
Now, in your app delegate class, you can use the viewControllers property of self.navigationController to return the view controllers in the navigation stack. The root controller will always be at index 0. viewControllers will return an NSArray, whose elements are of type NSObject, so you need to cast to a RootViewController pointer too. Here it is in two lines, to make the cast explicit:
// get a pointer to the root view controller
id obj = [self.navigationController.viewControllers objectAtIndex:0];
// cast it to a RootViewController pointer
RootViewController* rvc = (RootViewController*)obj;
// Now you can set the array
rvc.myArray = someArray;
(In order to use the RootViewController class name in your app delegate class you'll need to import the relevant header file - put this at the top of TableViewAppDelegate.m:
#import "RootViewController.h"
And that's it!
p.s. Bear in mind that declaring your myArray property as type (nonatomic, retain) means that your RootViewController object will end up pointing to the same instance of NSArray that you pass to it. So, for example:
NSMutableArray *array = [NSMutableArray arrayWithObjects: #"foo", #"bar", nil];
myRootViewController.myArray = array;
[array removeAllObjects]; // myRootViewController.myArray is now empty!
You can use (nonatomic, copy) instead, in which case your RootViewController object will make a copy of the array you pass in and retain that instead. Changes to the array once you've assigned it to your RootViewController object won't affect the copy.
navigationController.myArray = myArray;
before
[window addSubview:navigationController.view];