1

Here is my code:

I want to be able to create a global NSMutableArray that can store Budget* objects that can then be written to a .pList file... I'm only learning what pLists are, and I am a bit hazy about how to implement them...

Where am I going wrong here?

- (IBAction)btnCreateBudget:(id)sender 
{
    Budget *budget = [[Budget alloc] init];

    budget.name = self.txtFldBudgetName.text;
    budget.amount = [self.txtFldBudgetAmount.text intValue];    

    // Write the data to the pList
    NSMutableArray *anArray = [[NSMutableArray alloc] init]; // I want this to be a global variable for the entire app. Where do I put this?

    [anArray addObject:budget];

    [anArray writeToFile:[self dataFilePath] atomically:YES];

    /* As you can see, below is where I test the code. Unfortunately, 
    every time I run this, I get only 1 element in the array. I'm assuming 
    that this is because everytime the button is pressed, I create a brand new 
    NSMutableArray *anArray. I want that to be global for the entire app. */

    int i = 0;
    for (Budget * b in anArray)
    {
        i++;
    }
    NSLog(@"There are %d items in anArray",i);

}

-(NSString *) dataFilePath
{ 
    NSArray *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); 
    NSString *documentDirectory = [path objectAtIndex:0]; 
    return [documentDirectory stringByAppendingPathComponent:@"BudgetData.plist"];
}

edit: I'd like to add that I am creating the anArray array so that it can be accessible by other views. I understand that this can be done with NSNotification? or Should I do this the appDelegate classes? The end goal is to have the anArray object populate a UITableView that is in a separate View.

lorenzoid
  • 1,812
  • 10
  • 33
  • 51
  • why do you want it to be global and not an instance variable? – Carl Veazey Mar 06 '12 at 23:52
  • Not that I am against plist but have you put some thought into other storage formats, for example, JSON (with JSONKit)? If you plan to write this application for other devices (android), JSON would probably be the better choice. – Kurt Mar 06 '12 at 23:55
  • Why do you want the array to be global in scope? What other objects need access to it? – spring Mar 06 '12 at 23:58
  • I'm mostly doing this only as an exercise in the concepts of persistence in applications. But thank you @Prowla – lorenzoid Mar 06 '12 at 23:59
  • Well, I'd like to be able to populate a UITableView with objects created from the current view. @skinnyTOD – lorenzoid Mar 07 '12 at 00:00
  • If it is only accessible by the UITableView then have an array object in this view and set it from the current view with the created objects. Otherwise, if you would like many views to use the array you could look into using a singleton class or storage (sqlite, json, plist) but make sure that this is really necessary. – Kurt Mar 07 '12 at 00:07
  • possible duplicate of [How to define a global variable that can be accessed anywhere in my application?](http://stackoverflow.com/questions/6065965/how-to-define-a-global-variable-that-can-be-accessed-anywhere-in-my-application) – jww May 01 '14 at 10:10

4 Answers4

3

Just put the declaration outside the method instead of inside it.

NSMutableArray *anArray = nil;

- (IBAction)btnCreateBudget:(id)sender 
{
    ...
    if ( anArray == nil )
      anArray = [[NSMutableArray alloc] init]; 
    ...
}

If it's only used inside the one file, make it "static" instead to prevent name collisions with other files:

    static NSMutableArray *anArray = nil;

If it's only used inside the one method, make it "static" and put it inside that method:

- (IBAction)btnCreateBudget:(id)sender 
{
    static NSMutableArray *anArray = nil;
    ...
    if ( anArray == nil )
      anArray = [[NSMutableArray alloc] init]; 
    ...
}

Note that people usually use some kind of naming convention for global variables, like "gArray", to easily differentiate them from local variables, instance variables, or method parameters.

EricS
  • 9,650
  • 2
  • 38
  • 34
  • 1
    If the OP ever plans on multi-threading this code, it might be worth wrapping the initialisation in a `dispatch_once()` construct. – Aidan Steele Mar 07 '12 at 00:06
2

Global variable is not necessary in this case. You can do something like this:

  1. Read old data to mutable array (initWithContentsOfFile:).
  2. Add new record to the array.
  3. Save the array to same file.

But the second problem in your code is that if your Budget class is not a property list type (NSString, NSData, NSArray, or NSDictionary objects) writeToFile: will not save it sucessfully.

Artur Ozierański
  • 1,177
  • 7
  • 18
  • So how would I get a Budget object inside a property list type? – lorenzoid Mar 06 '12 at 23:57
  • 1
    "Archives and serializations guide" https://developer.apple.com/library/ios/#documentation/cocoa/conceptual/Archiving/Archiving.html You may also check out this topic: http://stackoverflow.com/questions/5413851/correct-way-to-save-serialize-custom-objects-in-ios – Artur Ozierański Mar 07 '12 at 00:00
1

You need to make sure that your Budget class invokes NSCoder and then the NSCoder initWithCoder: and NSCoder decodeWithCoder: methods. Otherwise, writeToFile: will not work for you NSObject class.

But I digress. The answer to the original question should be the following.

In your .h file you need to do the following.

@interface WhateverClassName : UIViewController 
{
    NSMutableArray *anArray;

}

@property(nonatomic, retain) NSMutableArray *anArray;
@end

Then, you need to make sure you @synthesize the NSMutableArray so that you don't get any freaky warnings. This is done just after the @implementation line in your .m file.

Then, within the function that you want it to be allocated into memory, simply do the following.

anArray = [[NSMutableArray alloc] initWithObjects:nil];

This is now a global variable. It is global in the sense that it can be used from any function and is not limited to use in one function.

Sebastien Peek
  • 2,528
  • 2
  • 23
  • 32
0

If you would like to have data accessible to the entire application or context ("global"), you can use a singleton. However, do this with care and make sure it is actually necessary and appropriate. I would suggest doing plenty of reading up on it prior to any implementation of a singleton. Carter Allen has a good basic implementation here.

According to "The end goal is to have the anArray object populate a UITableView that is in a separate View" you wouldn't need to write anything to a file, database or singleton. Just set the object. Like stated by Sebastien Peek.

If you wish for offline data storage, look into sqlite, json, plist , etc

Kurt
  • 7,102
  • 2
  • 19
  • 16
  • The OP does not want the `Budget` to be a "global" property, he wants an `NSMutableArray` to be. This answer has no relevance to the OP. – Sebastien Peek Mar 07 '12 at 00:20
  • Sure, but for what the OP is trying to achieve, this isn't necessary whatsoever. – Sebastien Peek Mar 07 '12 at 02:31
  • I did not state a singleton was necessary in these (vague) circumstances. OP mentioned he was mostly doing this as an exercise in persistence, thus, I assume to learn. So I thought I would bring up the concept of a singleton seeing the title is: "Where do I create global variables for an iOS app?" and a singleton was yet to be mentioned. – Kurt Mar 07 '12 at 03:09