Simple Save/Load the entire array - iphone

I'm new. Been trying to save/load a couple of arrays but to no avail.
I have "Global.h" and "Global.m" providing arrays for other classes.
in Global.h
extern NSMutableArray *arrayTable;
extern NSMutableArray *arrayPurpose;
#interface Global : NSObject <NSCoding> {
}
in Global.m
NSMutable *arrayTable;
MSMutable *arrayPurpose;
I have other views/controllers/classes/whatever that work with these arrays and they are functioning. What do I put inside this "Global" and the "AppDelegate.h" and "AppDelegate.m" so that these arrays are saved when this app goes into background and loads when the app starts? I need to use this "NSDocumentDirectory" because this data is important.
Please keep your explanations REALLLLLLY EASY. I have less than a week's experience. Thanks!!
Edit: Did what Joris suggested.
Added this to the AppDelegate
-(NSString *)archivePath {
NSString *docDir =
[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES)objectAtIndex: 0];
return [docDir stringByAppendingPathComponent:#"TableData.dat"];
}
- (void)applicationDidEnterBackground:(UIApplication *)application {
[NSKeyedArchiver archiveRootObject:arrayTable toFile:self.archivePath];
[NSKeyedArchiver archiveRootObject:arrayPurpose toFile:self.archivePath];
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[window addSubview:rootController.view];
[self.window makeKeyAndVisible];
arrayTable = [NSKeyedUnarchiver unarchiveObjectWithFile:[self archivePath]];
arrayPurpose = [NSKeyedUnarchiver unarchiveObjectWithFile:[self archivePath]];
if (arrayTable == nil) {
arrayPurpose = [[NSMutableArray alloc]init];
arrayTable = [[NSMutableArray alloc]init];
}
return YES;
}

Not very detailed but:
in the suspend method of your appdelegate you can use:
[NSKeyedArchiver archiveRootObject:arrayTable toFile:...];
and in the resume method:
arrayTable = [[NSKeyedUnarchiver unarchiveObjectWithFile:...] retain];

Related

IOS: Save Array when app is close or enter background

I have a cartArray(in AppDelegate.h #interface above) that need to be saved when the app in background mode or the app closed. The app worked fine when the cartArray has nothing but crashed when I added an item (Cart) in it and entered the background or closed the application by pressing the minus sign. My cartArray contains cart class in it.
May I know what is happening? The tutorial online is so complicated and I always find myself lost in the middle of explanation.
[AppDelegate.m]
- (void)applicationDidEnterBackground:(UIApplication *)application {
[AppDelegate saveData];
}
- (void)applicationWillEnterForeground:(UIApplication *)application {
[AppDelegate getData];
}
+(NSString *) getPathToAchieve{ NSLog(#"+++++getPathToAchieve");
static NSString* docsDir = nil;
if (docsDir == nil) {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
docsDir = [paths objectAtIndex:0];
}
NSString *fullFileName = [NSString stringWithFormat:#"%#.plist", docsDir];
return fullFileName;
}
- (void)applicationWillTerminate:(NSNotification *)notification{
[cartArray writeToFile:[AppDelegate getPathToAchieve] atomically:YES];
}
-(id)initWithCoder:(NSCoder *)aDecoder
{ self = [super init];
if (self != nil)
{
cartArray = [aDecoder decodeObjectForKey:#"cartArrayKeys"];
}
return self;
}
-(void)encodeWithCoder:(NSCoder *)anEncoder
{
[anEncoder encodeObject:cartArray forKey:#"cartArrayKeys"];
}
+(void)saveData{
[NSKeyedArchiver archiveRootObject:cartArray toFile:[self getPathToAchieve] ];
}
+(id)getData{
return [NSKeyedUnarchiver unarchiveObjectWithFile:[self getPathToAchieve]];
}
Your code is pretty messy. First, implement -(id)initWithCoder: and -(void)encodeWithCoder: in your Cart class, not AppDelegate class (and make sure Cart conforms to NSCoding protocol):
- (void) encodeWithCoder:(NSCoder *)encoder {
[encoder encodeObject:self.title forKey:#"title"];
[encoder encodeObject:self.description forKey:#"description"];
.....
}
- (id)initWithCoder:(NSCoder *)decoder {
if (self = [super init]) {
self.title = [decoder decodeObjectForKey:#"title"] ;
self.description = [decoder decodeObjectForKey:#"description"] ;
....
}
return self;
}
Second, implement -(void)saveData and -(void)getData:
-(void)saveData{
[[NSUserDefaults standardUserDefaults] setObject:[NSKeyedArchiver archivedDataWithRootObject:cartArray] forKey:#"cartArray"];
}
-(void)getData{
NSData *savedArray = [[NSUserDefaults standardUserDefaults] objectForKey:#"cartArray"];
if (savedArray != nil)
{
NSArray *oldArray = [NSKeyedUnarchiver unarchiveObjectWithData:savedArray];
if (oldArray != nil) {
cartArray = [[NSMutableArray alloc] initWithArray:oldArray];
} else {
cartArray = [[NSMutableArray alloc] init];
}
}
}
Call saveData when application is going to be terminated / entered background.
Call getData when application has loaded.
do all your saving in
- (void)applicationWillResignActive:(UIApplication *)application
from the documentation:
Tells the delegate that the application is about to become inactive.
This method is called to let your application know that it is about to
move from the active to inactive state. This can occur for certain
types of temporary interruptions (such as an incoming phone call or
SMS message) or when the user quits the application and it begins the
transition to the background state. An application in the inactive
state continues to run but does not dispatch incoming events to
responders. You should use this method to pause ongoing tasks, disable
timers, and throttle down OpenGL ES frame rates. Games should use this
method to pause the game. An application in the inactive state should
do minimal work while it waits to transition to either the active or
background state.
Array is a temporary storage if you want to save some data in permanent then try to use NSUSerDefult. After closing app array may vanish. NSUSerDefult data not vanish. NSUserDefault vanish only when app delete or Remove app from simulator.

NSMutableArray not accessible throughout my class?

I'm really frustrated after spending like three hours googling around to solve this problem!! It's probably an easy solution to it aswell.
I'm creating a really simple TableView app for the iPhone. It's supposed to fetch an XML-document and parse it (already fixed) and then put the data into objects called HPobject!
One HPobject represents one day of data from the XML-file. Anyhow!
Then I want the object to be stored in a NSMutableArray so I can grab it later when I'm creating the table rows. But I can't access it! My NSMutableArray is ALWAYS null! No matter what I do!
Here's my code:
//THE .h FILE
#import "TBXML.h"
#import "HPobject.h"
#interface RootViewController : UITableViewController {
NSMutableArray *listOfItems;
}
- (void)traverseElement:(TBXMLElement *)element;
//THE .m FILE
#import "RootViewController.h"
#import "HPobject.h"
#implementation RootViewController
- (void)viewDidLoad
{
NSMutableArray *listOfItems = [[NSMutableArray alloc] init];
TBXML * tbxml = [[TBXML tbxmlWithURL:[NSURL URLWithString:#"http://www.hpprov.se/istasts.php?q=xxxxxxx"]] retain];
if (tbxml.rootXMLElement)
[self traverseElement:tbxml.rootXMLElement]; //Works fine!
[tbxml release];
[super viewDidLoad];
}
- (void) traverseElement:(TBXMLElement *)element {
do {
//Lots of parsing code which all works fine and gets me the variables up next!
HPobject *currentObject = [[HPobject alloc] init];
currentObject.antalRegistrerade = numRegistrerade;
currentObject.inkomstBrutto = numBrutto;
currentObject.inkomstNetto = numNetto;
[listOfItems addObject:currentObject];
NSLog(#"Array: %#", listOfItems); //RETURNS null!
NSLog(#"Reg: %#, Net: %#, Brutt: %#", currentObject.antalRegistrerade, currentObject.inkomstNetto, currentObject.inkomstBrutto); //Returns the correct values!
NSLog(#"%d stycken!", listOfItems.count); //Returns 0!! :(
[currentObject release];
} while ((element = element->nextSibling));
}
You are defining listOfItems locally in viewDidLoad and then you try to access that in another method.
Make sure you are using an instance variable defined in your interface definition (header).
replace this line in viewDidLoad:
NSMutableArray *listOfItems = [[NSMutableArray alloc] init];
with
listOfItems = [[NSMutableArray alloc] init];
I suppose that listOfItems is an ivar. So to fix your problem change this:
NSMutableArray *listOfItems = [[NSMutableArray alloc] init];
to this
listOfItems = [[NSMutableArray alloc] init];
Take a look at the scope of your array. You have created that as a variable of another method. It will not visible in others. Make an instance var.

Objective C NSMutableDictionary memory management

I have a model class that keeps track record being built by multiple views. It has a NSMutableDictionary that has the fields and values I eventually write to the database. It is saved to a plist and loaded back when needed. I thought that I was keeping track of my memory, but it throws a EXC_BAD_ACCESS when I try to release the Dictionary. Here is my interface:
#import <Foundation/Foundation.h>
#interface CurrentEntryModel : NSObject {
NSMutableDictionary *currentEntry;
}
#property (nonatomic, retain) NSMutableDictionary *currentEntry;
- (void) setValue: (NSString *)value;
- (NSString *) getValue;
#end
My understanding is that currentEntry should be retained and I would have to release it during dealloc.
Here is my implementation (this isn't the entire class just the relevant parts):
#import "CurrentEntryModel.h"
#implementation CurrentEntryModel
#synthesize currentEntry;
-(id) init {
if ( self = [super init] )
{
//check for file
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *file;
file = #"location.plist";
if ([fileManager fileExistsAtPath:file]){
NSLog(#"file exists");
currentEntry = [[NSMutableDictionary alloc] initWithContentsOfFile:file];
}else {
NSLog(#"file doesn't exist");
currentEntry = [[NSMutableDictionary alloc ] initWithCapacity:1];
NSDate *testDate = [NSDate date];
[currentEntry setObject:testDate forKey:#"created"];
[currentEntry writeToFile:file atomically:YES];
}
}
return self;
}
- (void) setValue: (NSString *)value {
[currentEntry setObject:value forKey:#"location"];
}
- (NSString *) getValue {
return [currentEntry objectForKey:#"location"];
}
- (void) dealloc{
[currentEntry release];
[super dealloc];
}
#end
If I init this class it will automatically create the dictionary and if I call one of the set or get methods it seems like the dictionary is retained as it will dealloc correctly. If the class is just initialized and then no methods are called it will throw the EXC_BAD_ACCESS errors. If I am not mistaken when the file doesn't exist I don't initialize the dictionary correctly because the method starts with dictionary and not init. Although every time I run this the file is there so it always uses the the file found logic and I thought that that will retain the variable.
Am I not initializing the dictionary correctly?
Edit - changed the code on the convenience method to reflect the proper way. Everyone take note of what Squeegy has to say.
This is bad bad bad.
else {
NSLog(#"file doesn't exist");
currentEntry = [[NSMutableDictionary alloc ] dictionaryWithCapacity:1];
dictionaryWithCapacity: is a class method on NSMutableDictionary which returns an autoreleased object, and you don't retain it. So the run loop ends, and the dictionary gets autoreleased. Then you run [currentEntry release] in your dealloc and it explodes because that object is deallocated already.
you probably wan't initWithCapacity: instead. Always pair alloc with a method that starts with init.
Also, when using retained properties like this, I usually let the property figure this out for me, and only work with autoreleased objects. You just have to remember less rules, and there are less gotchas.
- (id)init {
// ...
self.currentEntry = [NSMutableDictionary dictionWithContentsOfFile:file];
// ...
}
- (void)dealloc {
//...
self.currentEntry = nil;
//...
}
This way you never have to call retain or release directly on the object. In my experience, this results in less confusing bugs. But it's also point of style among many ObjC programmer that not everyone agrees with.
Joshua -
+ (id)dictionaryWithCapacity:(NSUInteger)numItems
is a class method of NSDictionary. So when you call it, it should be:
[NSMutableDictionary dictionaryWithCapacity:1];
Not:
[[NSMutableDictionary alloc] dictionaryWithCapacity:1];
Further, [NSMutableDictionary dictionaryWithCapacity:] returns an autoreleased object. If you want to keep the dictionary as an ivar and not have it autoreleased on the next cycle of the run loop, you should call:
[currentEntry retain];
So, basically, change it to:
currentEntry = [[NSMutableDictionary alloc] initWithCapacity:1];
or:
currentEntry = [[NSMutableDictionary dictionaryWithCapacity:1] retain];
The first one probably makes more sense, since the connivence class methods were designed to be used when you wanted an autoreleased instance.

is this a bad Core Data pattern for sharing functionality?

I have an app and there is some basic functionality that needs to be shared across several controllers, one being adding and removing what I call BookmarkedObjects to my data store. So I created the following funciton and it seems to be working really well and I would like to apply this pattern to other function in my code like adding objects to an order, seeing if an order already exist ect..
Here is what I am doing:
+(void) RemoveBookmark: (NSString *) aItemID withCustomerNumber: (NSString *) aCustomerNumber withManufacturerID: (NSString *) aManufacturerID withManagedObjectContext: (NSManagedObjectContext *) aContext {
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:[NSEntityDescription entityForName:#"BookmarkedItem" inManagedObjectContext:aContext]];
[request setPredicate:[NSPredicate predicateWithFormat:#"CustomerNumber==%# AND ManufacturerID==%# AND ItemID==%#", aCustomerNumber, aManufacturerID, aItemID]];
[request setFetchLimit:1];
NSError *error = nil;
NSArray *results = [aContext executeFetchRequest:request error:&error];
[request release], request = nil;
[aContext deleteObject:[results lastObject]];
[aContext save:&error];
}
I would suggest you to make a singleton class and add all such type of function in it.The main benefit of its is that all class methods are loaded on compile time so if there are 5 such functions the your app will take up unnecessary memory, so I'll suggest you something
like this
Define a class named SharedFunctions
in .h
#interface SharedFunctions : NSObject
{
}
+(SharedFunctions*)sharedInstance;
-(void)yourFunction;
.....
//make as many instace methods as you want
#end
in .m
#implementation SharedFunctions
static SharedFunctions* singletonInstance = nil;
-(id)init
{
if (self = [super init])
{
}
return self;
}
+(SharedFunctions*)sharedInstance
{
#synchronized(self) {
if (singletonInstance == nil)
{
singletonInstance = [[SharedFunctions alloc] init];
}
}
return singletonInstance;
}
///and define your methods
-(void)yourFunction
{
//somthing
}
#end
Now to use your methods just call
[[SharedFunctions sharedInstance] yourFunction];
By this way all such methods are at one place and there is only one class method so no memory issue.

iPhone - Objective-C - Memory Leak with initWithArray

I am using the code below to set my two NSArray ivars:
The issue is, I keep getting a memory leak on the following lines:
followingFriendsArray = [[NSArray alloc] initWithArray:friend.Following];
followerFriendsArray = [[NSArray alloc] initWithArray:friend.Followers];
Is this not the correct way to set ivars from an existing NSArray of items? Any help would be appreciated. I've also tried to autorelease the above two lines, but when I actually access them in another method I get an error that they've already been released.
I have included my Interface and Implementation code below:
Interface .h:
NSArray *followingFriendsArray;
NSArray *followerFriendsArray;
#property (nonatomic, retain) NSArray *followingFriendsArray;
#property (nonatomic, retain) NSArray *followerFriendsArray;
Implementation .m:
- (void)handlerGetFollowingInformation:(id)value {
BOOL success = [Utility checkWebServiceErrors:value controller:self.navigationController];
if (success) {
Friend *friend = (Friend *)value;
followingFriendsArray = [[NSArray alloc] initWithArray:friend.Following];
followerFriendsArray = [[NSArray alloc] initWithArray:friend.Followers];
}
}
This is how I need to access the arrays:
- (void)followersButtonTapped:(id)sender {
FollowingVC *fvc = [[FollowingVC alloc] initWithNibName:#"FollowingViewController" bundle:nil];
fvc.friends = followerFriendsArray;
[self.navigationController pushViewController:fvc animated:YES];
[fvc release];
}
I release my two ivars in the following way as per usual:
- (void)viewDidUnload {
self.followingFriendsArray = nil;
self.followerFriendsArray = nil;
[super viewDidUnload];
}
- (void)dealloc {
[followingFriendsArray release];
[followerFriendsArray release];
[super dealloc];
}
I mean the code works just fine, it's just that I'm concerned about said memory leaks when I run the "Leaks" performance tool.
OK
you should not use autorelease in this case, but you have to release the arrays by calling :
[followingFriendsArray release];
[followerFriendsArray release];
you can do it:
when you don't need to use them any more.
in the dealloc method in your .m file.
option 2looks like that -
- (void)dealloc {
[followingFriendsArray release];
[followerFriendsArray release];
[super dealloc];
}
BTW -
if you don't manipulate the arrays after creating them (add / remove objects) you should use an immutable array (NSArray).
Good Luck
Your method handlerGetFollowingInformation is assigning new values to followingFriendsArray and followerFriendsArray without releasing the previous contents. If you call this method more than once on the same instance you will leak.
CRD is right that the arrays are not released inside the handlerGeFollowingInformation method but the fix is maybe overkill. What you need to do is to use self. so that the setter method is called which does that automatically. You could should look like this:
- (void)handlerGetFollowingInformation:(id)value {
BOOL success = [Utility checkWebServiceErrors:value controller:self.navigationController];
if (success) {
Friend *friend = (Friend *)value;
self.followingFriendsArray = [[NSArray alloc] initWithArray:friend.Following];
self.followerFriendsArray = [[NSArray alloc] initWithArray:friend.Followers];
}
}
Easy fix but hard to spot and I ran into this issue over and over again especially when I started to dealloc are the properties.
-Andy