Loading data once the data is in array - objective-c

[array addObject:textdata.text];
NSUserDefaults *save = [NSUserDefaults standardUserDefaults];
[save setObject:array forKey:#"success" ];
[save synchronize];
-(void) viewDidLoad
NSUserDefaults *viewdata1 = [NSUserDefaults standardUserDefaults];
[viewdata1 objectForKey:#"success"];
[viewdata1 synchronize];
[tabledata reloadData];
Once the data is saved in the array, how do I upload it once the app runs again? I want the data to load back in the table once.

The first step is to retrieve it from user defaults. The second step is not to drop it on the floor.
[viewdata1 objectForKey:#"success"];
This does one, but not the other: You retrieve it, but then you drop it on the floor.
You need to store the object as the value of a property (which means you will need to declare a property for that purpose), then, in your table view's data source, return the count of that array as your number of rows and objects in the array (or properties of those objects) as the row values.
Also, you shouldn't need to call synchronize, especially after retrieving the value.

You should make like this:
TSTableViewController.h:
#property(nonatomic, readwrite, retain) NSMutableArray* dataSource;
TSTableViewController.m:
- (id) init
{
if ((self = [super init]))
{
[[NSNotificationCenter defaultCenter] addObserver: self selector: #selector(applicationDidEnterBackground:)
name: UIApplicationDidEnterBackgroundNotification
object: nil];
}
return self;
}
- (void) applicationDidEnterBackground: (NSNotification*) notification
{
[[NSUserDefaults standardUserDefaults] setObject: self.dataSource
forKey: #"success" ];
}
- (void) viewDidLoad
{
[super viewDidLoad];
NSArray* array = [[NSUserDefaults standardUserDefaults] objectForKey: #"success"];
if (array)
{
self.dataSource = [NSMutableArray arrayWithArray: array];
}
else
{
self.dataSource = [[[NSMutableArray alloc] init] autorelease];
}
[tableView reloadData];
}
- (void) addDataToDataSource
{
[self.dataSource addObject: textdata.text];
[tabledata reloadData];
}
- (void) dealloc
{
[dataSource release];
dataSource = nil;
[super dealloc];
}

Related

NSUserDefaults unable to save and read my custom objects

I have a class Notification that implements the NSCoding protocol.
I have an array of notifications.I am trying to save the notifications with NSUserDefaults.In my app delegate notifications is a NSMutableArray that contains the Notification objects.That's my app delegate:
+ (void) initialize
{
NSUserDefaults* defaults=[NSUserDefaults standardUserDefaults];
[defaults registerDefaults: [NSDictionary dictionaryWithObject: [NSKeyedArchiver archivedDataWithRootObject: [NSArray array]] forKey: #"notificationsData"]];
}
- (id) init
{
self=[super init];
if(self)
{
NSUserDefaults* defaults=[NSUserDefaults standardUserDefaults];
NSData* notificationsData=[defaults objectForKey: #"notificationsData"];
notifications= [[NSKeyedUnarchiver unarchiveObjectWithData: notificationsData]mutableCopy];
}
return self;
}
- (void) applicationWillTerminate:(NSNotification *)notification
{
NSUserDefaults* defaults=[NSUserDefaults standardUserDefaults];
NSData* notificationsData=[NSKeyedArchiver archivedDataWithRootObject: notifications];
[defaults setObject: notificationsData forKey: #"notificationsData"];
}
In the Notification class text and title are of type NSString (both readwrite), and date is of type NSDate (also this has readwrite property).This is how I implement the NSCoding protocol:
- (void) encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeObject: date forKey: #"date"];
[aCoder encodeObject: title forKey: #"title"];
[aCoder encodeObject: text forKey: #"text"];
}
- (id) initWithCoder:(NSCoder *)aDecoder
{
self=[super init];
if(self)
{
date=[aDecoder decodeObjectForKey: #"data"];
title=[aDecoder decodeObjectForKey: #"title"];
text=[aDecoder decodeObjectForKey: #"text"];
}
return self;
}
So I have these problems:
When the application terminates I get EXC_BAD_ACCESS in the
Notification class, when I try to encode text with NSKeyedArchiver;
The notifications aren't saved and the array is always long zero
when the application starts.
Update: With more debug I discovered where the application crashes.There is more code to see (I'm using a table view to display the data):
- (NSInteger) numberOfRowsInTableView:(NSTableView *)tableView
{
return [notifications count];
}
- (id) tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
// Debug
id obj=[[notifications objectAtIndex: row] valueForKey: [tableColumn identifier]];
Class class=[obj class];
// What you see above is just for debug purposes
return [[notifications objectAtIndex: row] valueForKey: [tableColumn identifier]];
}
- (void) tableViewSelectionDidChange:(NSNotification *)notification
{
NSInteger row=[tableView selectedRow];
if(row >= 0 && row< [notifications count])
[removeButton setEnabled: YES];
else
[removeButton setEnabled: NO];
}
The last method called is this:
- (id) tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row;
Probably the problem is that the data is somehow corrupted and the value returned by this method is not valid.Anyway the app doesn't crash in this method, but after this method.
If I load two objects from user defaults, only one object gets loaded before crashing (so the method gets called once).
However I'm still unable to get the real reason of the crash.
More code:
- (IBAction) addNotification :(id)sender
{
Notification* notification=[[Notification alloc]init];
[notification setDate: [datePicker dateValue]];
[notification setText: [textView string]];
[notifications addObject: notification];
[tableView reloadData];
}
- (IBAction)removeNotification:(id)sender
{
[notifications removeObjectAtIndex: [tableView selectedRow]];
[tableView reloadData];
}
addNotification and removeNotification are both triggered by buttons.
EDIT: I discovered that I wasn't using ARC, but even if I turn it on the app crashes.
In the line:
date=[aDecoder decodeObjectForKey: #"data"];
#"data" doesn't match the encoder key. You really want:
date=[aDecoder decodeObjectForKey: #"date"];
You might need to call [NSUserDefaults synchronize] since it will not happen automatically when the application suddenly exits:
- (void) applicationWillTerminate:(NSNotification *)notification
{
NSUserDefaults* defaults=[NSUserDefaults standardUserDefaults];
NSData* notificationsData=[NSKeyedArchiver archivedDataWithRootObject: notifications];
[defaults setObject: notificationsData forKey: #"notificationsData"];
[defaults synchronize];
}
You couldn't guess what was wrong: my bad, I forgot to enable ARC and some objects were released when they shouldn't.

NSMutableArray to NSString and Passing NSString to Another View IOS5.1

I have an NSMutableArray of names. I want the pass the data (selected name) inside of NSMutableArray as text to another view's label.
FriendsController.m:
- (void)viewDidLoad {
[super viewDidLoad];
arrayOfNames=[[NSMutableArray alloc] init];
arrayOfIDs=[[NSMutableArray alloc] init];
userName=[[NSString alloc] init];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
long long fbid = [[arrayOfIDs objectAtIndex:indexPath.row]longLongValue];
NSString *user=[NSString stringWithFormat:#"%llu/picture",fbid];
[facebook requestWithGraphPath:user andDelegate:self];
userName=[NSString stringWithFormat:#"%#",[arrayOfNames objectAtIndex:indexPath.row]];
FriendDetail *profileDetailName = [[FriendDetail alloc] initWithNibName: #"FriendDetail" bundle: nil];
profileDetailName.nameString=userName;
[profileDetailName release];
}
- (void)request:(FBRequest *)request didLoad:(id)result {
if ([result isKindOfClass:[NSData class]]) {
transferImage = [[UIImage alloc] initWithData: result];
FriendDetail *profileDetailPicture = [[FriendDetail alloc] initWithNibName: #"FriendDetail" bundle: nil];
[profileDetailPicture view];
profileDetailPicture.profileImage.image= transferImage;
profileDetailPicture.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
[self presentModalViewController:profileDetailPicture animated:YES];
[profileDetailPicture release];
}
}
In FriendDetail.h
NSString nameString;
IBOutlet UILabel *profileName;
#property (nonatomic, retain) UILabel *profileName;
#property (nonatomic, retain) NSString *nameString;
In FriendDetail.m
- (void)viewDidLoad
{
[super viewDidLoad];
profileName.text=nameString;
}
nameString in second controller(FriendDetail) returns nil. When i set a breakpoint in firstcontroller I see the string inside of nameString is correct but after that it returns to nil somehow.
-----------------------EDIT----------------------------------------
According to answers I have improved my code little bit
FriendsController.h
FriendDetail *friendController;
#property (strong, nonatomic) FriendDetail *friendController;
FriendsController.m
- (void)viewDidLoad
{
[super viewDidLoad];
arrayOfNames=[[NSMutableArray alloc] init];
arrayOfIDs=[[NSMutableArray alloc] init];
arrayOfThumbnails=[[NSMutableArray alloc] init];
userName=[[NSString alloc] init];
friendController= [[FriendDetail alloc] initWithNibName: #"FriendDetail" bundle: nil];
}
-(void)request:(FBRequest *)request didLoad:(id)result{
if ([result isKindOfClass:[NSData class]])
{
transferImage = [[UIImage alloc] initWithData: result];
friendController.nameString=userName;
[friendController view];
friendController.profileImage.image= transferImage;
friendController.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
[self presentModalViewController:friendController animated:YES];
}
//this is how i take facebook friends list
if ([result isKindOfClass:[NSDictionary class]]){
items = [[(NSDictionary *)result objectForKey:#"data"]retain];
for (int i=0; i<[items count]; i++) {
NSDictionary *friend = [items objectAtIndex:i];
long long fbid = [[friend objectForKey:#"id"]longLongValue];
NSString *name = [friend objectForKey:#"name"];
NSLog(#"id: %lld - Name: %#", fbid, name);
[arrayOfNames addObject:[NSString stringWithFormat:#"%#", name]];
[arrayOfIDs addObject:[NSNumber numberWithLongLong:fbid]];
}
}
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
long long fbid = [[arrayOfIDs objectAtIndex:indexPath.row]longLongValue];
NSString *user=[NSString stringWithFormat:#"%llu/picture",fbid];
userName=[NSString stringWithFormat:#"%#",[arrayOfNames objectAtIndex:indexPath.row]];
[facebook requestWithGraphPath:user andDelegate:self];
[username retain]
}
Now when i select row first time it sends name. When i come back to tableview and select another name it shows the old name.
If I delete [username retain] in didSelectRowAtIndexPath: it still sends nil to nameString
when I set break point at didSelectRowAtIndexPath: at line `
userName=[NSString stringWithFormat:#"%#",[arrayOfNames objectAtIndex:indexPath.row]]`
I can see userName = #"Adam Dart" which is correct
in my second breakpoint at line friendController.nameString=userName; I see that nameString =nil and userName = Variable is not CFString
ARC is set to NO
The value is nil because you did not pass the value in request:didLoad: function.
In function didSelectRowAtIndexPath, You create a local instance of another ViewController and set the value of nameString, but you did not present the view and release the ViewController immediately. You actually do nothing in these few lines of code:
FriendDetail *profileDetailName = [[FriendDetail alloc] initWithNibName: #"FriendDetail" bundle: nil];
profileDetailName.nameString = userName;
[profileDetailName release];
In function request:didLoad:, again you create a local instance of another ViewController with image. But this instance is only local to this function, which means no relation to the one created in didSelectRowAtIndexPath.
What you need to do is, remember the name of clicked row first in didSelectRowAtIndexPath, here you dont have to create the ViewController instance. When the request finish, set both the image and name to the controller and then present it. But you should avoid user from clicking different rows at the same time, because you don't know when the request finish.
You have two instances of FriendDetail called profileDetailPicture. Both of theses profileDetailPicture are not the same. So in your didSelectRowAtIndexPath method, the value that you assigned to the nameString will not be visible/available to the nameString of the profileDetailPicture In the request:(FBRequest *)request didLoad method.
Edit for solution:
Create an iVar or property (profileDetailPicture) in the FriendController.
Only do one allocation in the request:(...) method.
Remove the allocation statement in the didSelectRowAtIndexPath.
Any chance it has to do with the fact that you assign to profileDetailName and then immediately release it?
profileDetailName.nameString=userName;
[profileDetailName release];
You have to allocate the "first_controller" in your "second_controller"
to pass objects such as your string. and you would call the nameString differently.
example:
second_controller.h
#import "first_controller.h"
...
#interface second_controller : UIViewController{
first_controller* firstController;
}
second_controller.m
- (void)viewDidLoad {
[super viewDidLoad];
firstController = [[first_controller alloc] init];
profileName.text = firstController.nameString;
}
Which you'll have to init it correctly, because its two views sharing information.

How to store CCSprite in NSUserDefault

I have a strange problem while working with CCSprite subclass Creature.
Lets,my object is Creature* creature;
The class Creature declaration-
#interface Creature : CCSprite <NSCoding>{
int creatureAge;
NSString *creatureName;
}
Implementation
+(id)initializeCreatureWithType:(int)type
{
Creature *creature = nil;
creature = [[[super alloc] initWithFile:[NSString stringWithFormat:#"ch%i_default.png",type]]autorelease];
return creature;
}
The problem is when i store my Creature class object 'creature' in NSUserDefault using-
- (void)encodeWithCoder:(NSCoder *)encoder {
[encoder encodeObject:self.creatureName forKey:#"creatureName"];
[encoder encodeInt:self.creatureAge forKey:#"creatureAge"];
}
And the decode it with-
- (id)initWithCoder:(NSCoder *)decoder {
if((self = [super init])) {
self.creatureName = [decoder decodeObjectForKey:#"creatureName"];
self.creatureAge= [decoder decodeIntForKey:#"creatureAge"];
}
Then save the creature object using-
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSData *myEncodedObject = [NSKeyedArchiver archivedDataWithRootObject:creature];
[defaults setObject:myEncodedObject forKey:#"my_pet"];
And the load-
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSData *myEncodedObject = [defaults objectForKey:#"my_pet"];
Creature* newcreature = (Creature *)[NSKeyedUnarchiver unarchiveObjectWithData: myEncodedObject];
The problem is that when I load this i get the property value of previously stored creature, but the image that is assigned to previous creature perhaps does not copied. Because if i add the newcreature to any CCLayer it does not display any image, though it get the property value of previous creature.
What can I do now to get the newcreature with image? is it needed to add image name as a separate property???
You could simply store the type as well and then do it like this:
#interface Creature : CCSprite {
int creatureType;
int creatureAge;
NSString *creatureName;
}
+ (id)creatureWithType:(int)type;
- (id)initWithCreatureType:(int)type;
#end
#implementation Creature
+ (id)creatureWithType:(int)type
{
return [[[[self class] alloc] initWithCreatureType:type] autorelease];
}
- (id)initWithCreatureType:(int)type
{
self = [super initWithFile:[NSString stringWithFormat:#"ch%i_default.png", type]];
if (!self) return nil;
creatureType = type;
return self;
}
- (id)initWithCoder:(NSCoder *)decoder {
int type = [decoder decodeIntForKey:#"creatureType"];
self = [self initWithCreatureType:type];
if (!self) return nil;
self.creatureName = [decoder decodeObjectForKey:#"creatureName"];
self.creatureAge= [decoder decodeIntForKey:#"creatureAge"];
return self;
}
- (void)encodeWithCoder:(NSCoder *)encoder
{
[encoder encodeInt:creatureType forKey:#"creatureType"];
[encoder encodeObject:self.creatureName forKey:#"creatureName"];
[encoder encodeInt:self.creatureAge forKey:#"creatureAge"];
}
#end
You might want to expose creatureType via a property as well. Note that instead of initializeCreatureWithType: it's "more Cocoa" to use the name creatureWithType:.

Storing NSMutableArray of UIViews in NSUserDefaults

I have read several posts about this but I'm not able to fix the error. If someone could please help.
Here is the code I use. I have an NSMutableArray called list.
-(void) awakeFromNib
{
prefs=[[NSUserDefaults standardUserDefaults]retain];
if ([prefs arrayForKey:#"list"]) {
list=[[NSMutableArray alloc]initWithArray:[prefs objectForKey:#"list"]];
}
else {
list=[[NSMutableArray alloc]init];
}
}
-(void)saveData
{
NSLog(#"saving data!");
[prefs setObject:list forKey:#"list"];
}
- (void)dealloc {
[self saveData];
[prefs synchronize];
[prefs release];
}
You cannot store UIView instances in the user defaults, but only objects that can be serialized in a property list (see here) Also, as #Justin said, do not retain or release the defaults object.
Thank you. However, I had earlier read that you cannot save an NSMutableArray in NSUserDefaults so I attempted to convert it to NSData and then use it.
Here's my ViewController.m file:
-(id)initWithCoder:(NSCoder *)aDecoder
{
self=[[calViewController alloc]init];
if (self!=nil) {
self.list=[aDecoder decodeObjectForKey:#"list"];
}
return self;
}
-(void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeObject:list forKey:#"list"];
}
And here's my AppDelegate.m file:
-(void) applicationDidFinishLaunching:(UIApplication *)application
{
NSLog(#"Application did finish launching");
defaults=[NSUserDefaults standardUserDefaults];
NSData *dataRepresentingSavedArray=[defaults objectForKey:#"lastArray"];
if (dataRepresentingSavedArray!=nil) {
NSArray *oldSavedArray = [NSKeyedUnarchiver unarchiveObjectWithData:dataRepresentingSavedArray];
if (oldSavedArray != nil)
{
listAr = [[NSMutableArray alloc] initWithArray:oldSavedArray];
else
{
listAr = [[NSMutableArray alloc] init];
NSLog(#"listAr: %#",listAr);
}
}
}
-(void) applicationWillTerminate:(UIApplication *)application
{
[[NSUserDefaults standardUserDefaults] setObject:[NSKeyedArchiver archivedDataWithRootObject:listAr] forKey:#"lastArray"];
}
I've never used UserDefaults before and I absolutely confused. I've read all the docs but I don't seem to be getting it right!
EDIT:
-(void) applicationDidFinishLaunching:(UIApplication *)application
{
NSLog(#"Application did finish launching");
defaults=[NSUserDefaults standardUserDefaults];
if ([defaults objectForKey:#"lastArray"]) {
NSData *dataRepresentingSavedArray=[defaults objectForKey:#"lastArray"];
if (dataRepresentingSavedArray!=nil) {
NSArray *oldSavedArray = [NSKeyedUnarchiver unarchiveObjectWithData:dataRepresentingSavedArray];
if (oldSavedArray != nil)
listAr = [[NSMutableArray alloc] initWithArray:oldSavedArray];
}
}
else
{
listAr = [[NSMutableArray alloc] init];
NSLog(#"listAr: %#",listAr);
}
}

NSCFArray leak in the NSMutablearray allocation

I am getting the leak at this allocation
filteredListContent = [[NSMutableArray alloc] initWithCapacity:[showList count]];
CODE:
-(void)reloadTable
{
EventListAppDelegate *appDelegate;
UIApplication app = [UIApplication sharedApplication];
appDelegate = (EventListAppDelegate *)[app delegate];
contactList = [appDelegate getAllContactsList];
inviteeList = [appDelegate getInviteeListForEvent:event.primaryKey];
if (isInvited == YES)
{
showList = [appDelegate getInviteeListForEvent:event.primaryKey];
}
else
{
showList = [appDelegate getAllContactsList];
}
filteredListContent = [[NSMutableArray alloc] initWithCapacity:
[showList count]];
[filteredListContent addObjectsFromArray: showList];
[self organizeContactItemsIntoIndexes];
self.title = [event.name capitalizedString];
[self getToolbar];
[theTableView reloadData];
}
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
{
[filteredListContent removeAllObjects];
ContactDTO *currentElement;
NSRange range;
for (currentElement in showList)
{
range = [currentElement.lastName rangeOfString:searchText
options:NSCaseInsensitiveSearch];
if(range.location == 0)
{
[filteredListContent addObject:currentElement];
}
}
[self organizeContactItemsIntoIndexes];
[theTableView reloadData];
}
- (void)dealloc
{
[filteredListContent release];
[super dealloc];
}
Your code will allocate a new instance of filteredListContent every time reloadTable is called, which will usually happen several times during the lifetime of your application. This causes a leak because the old instances are not released.
The best (and easiest) way to fix it would be to make filteredListContent a retain property:
in your class header:
#property (nonatomic, retain) NSMutableArray * filteredListContent;
in your reloadTable method:
self.filteredListContent = [NSMutableArray arrayWithCapacity:[showList count]];
Note the use of self. in the second code snippet. That syntax informs Cocoa that it should use the property accessor to set the value of filteredListContent, which will then send the appropriate retain and release messages for you.
You've posted three nearly-identical questions pertaining to memory leaks. It might be helpful for you to read through Apple's Memory Management Programming Guide.