Cannot figure out why my app crashes when I use NSKeyedArchivers / NSKeyedUnarchivers - iphone

I am developing my first iphone 'Diary' app, which uses custom 'Entry' objects that hold an NSString title, NSString text and NSDate creationDate. When I try to archive an NSMutableArray of Entry objects, and later retrieve them the next time the view loads, the app crashes. I have gone through a bunch of sample codes and examples that use NSKeyedArchivers, but still couldn't figure out why that happens. I am guessing there is a problem with the initialization of the array that holds the entries but not sure...
Here is the code, maybe you could find something that I have persistently overseen..."
//--------- Entry.m---------------
- (id) initWithCoder:(NSCoder *)aDecoder{
if ((self = [super init])) {
self.title = [[aDecoder decodeObjectForKey:#"title"] retain];
self.text = [[aDecoder decodeObjectForKey:#"text"] retain];
self.created = [[aDecoder decodeObjectForKey:#"created"] retain];
}
return self;
}
- (void) encodeWithCoder:(NSCoder *)aCoder{
[aCoder encodeObject:self.title forKey:#"title"];
[aCoder encodeObject:self.text forKey:#"text"];
[aCoder encodeObject:self.created forKey:#"created"];
}
//-------------- Diary View Controller.m
- (NSString *)dataFilePath {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
return [documentsDirectory stringByAppendingPathComponent:kFilename];
}
- (void) writeDataToArchive {
NSMutableData *data = [[NSMutableData alloc] init];
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc]
initForWritingWithMutableData:data];
[archiver encodeObject:self.entriesArray forKey:#"entriesArray"];
[archiver finishEncoding];
BOOL result = [data writeToFile:[self dataFilePath] atomically:YES];
[archiver release];
[data release];
}
- (void)addItem:sender {
int count = [entriesArray count] +1;
NSString *newEntryTitle = [NSString stringWithFormat:#"Entry %d", count];
Entry *anEntry = [[Entry alloc] initWithTitle:newEntryTitle text:#"-"
created:[NSDate date]];
[entriesArray addObject:anEntry];
[self.tableView reloadData];
[anEntry release];
[self writeDataToArchive];
}
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *filePath = [self dataFilePath];
if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
NSData *data = [[NSMutableData alloc]
initWithContentsOfFile:[self dataFilePath]];
NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc]
initForReadingWithData:data];
NSMutableArray *array = [unarchiver decodeObjectForKey:#"entriesArray"];
entriesArray = [array mutableCopy];
[array release];
[unarchiver finishDecoding];
[unarchiver release];
[data release];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:
(NSIndexPath *)indexPath
{
// ... some other stuff
NSUInteger row = indexPath.row;
Entry *entry = [entriesArray objectAtIndex:row];
cell.textLabel.text = entry.title;
return cell;
}
Thanks a lot.

When you read an array back out with NSKeyedUnarchivers you always get an unmutable copy back. You would need to declare *array as NSArray or just get rid of array all together.
entriesArray = [[unarchiver decodeObjectForKey:#"entriesArray"] mutableCopy];
And #JeremyP points out another issue. Since you didn't alloc or retain *array you should not release it.

You should not release array in viewDidLoad because you do not own it.
Please review the Cocoa memory management Rules because there are a couple of other memory management issues in your code. In particular,
self.title = [[aDecoder decodeObjectForKey:#"title"] retain];
self.text = [[aDecoder decodeObjectForKey:#"text"] retain];
self.created = [[aDecoder decodeObjectForKey:#"created"] retain];
in your initWithCoder: method all leak on the assumption the properties are retain or copy.

Related

Implementing a Search History feature in iOS

I've got a search page at the moment which will load a list of results for a web-service, but when I return to the search page I would like to 'save' whatever was entered (e.g. 'resto italian') and then display that entry and previous entries into a table view below, like in my following image:
My plan was to use property list serialization - if there isn't already a list, create a property list called history.plist, and populate it with each search term that is made, and display the nearest ten in the table view like above.
What I've tried:
// should create history.plist
- (NSString *)dataFilePath {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentationDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
return [documentsDirectory stringByAppendingString:#"history.plist"];
}
/* This is the action for when 'search' is clicked - calls the method above to create
a new plist if it's not already created.
I then try to display the contents of the of the file in the textfield itself
(for testing purposes) but it's not saving/displaying properly at the moment. */
- (IBAction)saveHistory:(id)sender {
NSString *filePath = [self dataFilePath];
if([[NSFileManager defaultManager] fileExistsAtPath:filePath])
{
NSArray *array = [[NSArray alloc] initWithContentsOfFile:filePath];
for (int i = 0; i < (sizeof(array)); i++) {
UITextField *theField = self.searchHistory;
theField.text = [NSString stringWithFormat:#"%#", array];
}
}
UIApplication *app = [UIApplication sharedApplication];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(applicationWillResignActive:) name:UIApplicationWillResignActiveNotification object:app];
}
Any links to tutorials attempting to do this, suggestions towards what I should do, or improvements to what I have would be greatly appreciated.
This should fix the problem:
// This is inside viewDidLoad
UIApplication *myApp = [UIApplication sharedApplication];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(applicationDidEnterBackground:)
name:UIApplicationDidEnterBackgroundNotification
object:myApp];
// This is inside my table view - where I'm loading the file data to display in table cells
NSString *myPath = [self dataFilePath];
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:myPath];
if (fileExists) {
NSArray *values = [[NSArray alloc] initWithContentsOfFile:myPath];
for (int i = 0; i < values.count; i++) {
cell.historyDisplay.text = [NSString stringWithFormat:#"%#", [values objectAtIndex:i]];
}
}
// This is the file path for history.plist
- (NSString *)dataFilePath {
NSArray *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
return [[path objectAtIndex:0] stringByAppendingString:#"history.plist"];
}
// This is my search button - I want to save whatever was typed in the text field, into history.plist, to display in my tableview whenever a user goes to it.
- (IBAction)saveHistory:(id)sender {
NSMutableArray *values = [[NSMutableArray alloc]initWithContentsOfFile:[self dataFilePath]];
if(searchInputTextField.text.length > 0)
[values addObject:searchInputTextField.text];
[values writeToFile:[self dataFilePath] atomically:YES];
[leTableView reloadData];
}
I would use my suggest in comments, but here's some edits to your code that might help in the meantime.
NSArray *array = [[NSArray alloc] initWithContentsOfFile:filePath];
for (int i = 0; i <array.count; i++) {
//I don't know what this line means
UITextField *theField = self.searchHistory;
//Change this line to this
theField.text = [NSString stringWithFormat:#"%#", [array objectAtIndex:i]];
}
I would use Core Data, creating a class, i.e. HistoryRecord with attributes termSearched and timestamp of type NSString and NSDate respectively.
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#interface HistoryRecordManagedObject : NSManagedObject
#property (nonatomic, retain) NSString *termSearched;
#property (nonatomic, retain) NSDate *timestamp;
+ (NSArray *)findEntity:(NSString *)entity withPredicate:(NSPredicate *)predicate
#end
Implementation
#import "HistoryRecordManagedObject.h"
#implementation HistoryRecordManagedObject
#dynamic termSearched;
#dynamic timstamp;
+ (NSArray *)findEntity:(NSString *)entity withPredicate:(NSPredicate *)predicate
{
NSError *error;
NSArray *fetchedObjects;
/* After set all properties, executes fetch request */
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entityDesc = [NSEntityDescription entityForName:entity
inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entityDesc];
[fetchRequest setPredicate:predicate];
fetchedObjects = [managedObjectContext executeFetchRequest:fetchRequest error:&error];
[fetchRequest release];
return fetchedObjects;
}
#end
Of course that's not just this! There are some extra stuff that must be done to use Core Data such as create the model. Read a little about it! It's worth!
Good luck!
In the action for searching, just save the search result to NSUserDefaults.
NSMutableArray *searches = [[NSUserDefaults standardUserDefaults] arrayForKey:#"searches"];
[searches insertObject:textField.text atIndex:0];
[[NSUserDefaults standardUserDefaults] setObject:searches forKey:#"searches"];
[[NSUserDefaults standardUserDefaults] synchronize];
Then load the same array for the tables data source and reload the table in viewwillappear and when keyboard is dismissed.
Replace your saveHistory function by below way:
- (IBAction)saveHistory:(id)sender
{
NSMutableArray *values = [[NSMutableArray alloc]initWithContentsOfFile:[self dataFilePath]];
if(searchInputTextField.text.length > 0)
[values addObject:searchInputTextField.text];
[values writeToFile:[self dataFilePath] atomically:YES];
[leTableView reloadData];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return values.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier] autorelease];
}
cell.textLabel.text = [values objectAtIndex:indexPath.row];
}

read and write using NSKeyedArchiver, ios

Below is a class to read and write data using nsarchive
Data.m
-(id)init {
self = [super init];
if(self) {
arr = [[NSMutableArray alloc] init];
}
return self;
}
-(NSString *)getPath {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentPath;
if ([paths count] > 0)
documentPath = [paths objectAtIndex:0];
NSString *draftDataPath = [documentPath stringByAppendingPathComponent:#"draftData.dat"];
return draftDataPath;
}
-(void)saveDataToDisk {
NSString *path = [self getPath];
[NSKeyedArchiver archiveRootObject:arr toFile:path];
}
-(void)loadDataFromDisk {
NSString *path = [self getPath];
self.arr = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
}
At later on, I am adding some objects into arr by doing
CustomerClass.m
- (void) viewDidLoad {
Data *data = [[Data alloc] init];
[data.arr addObject:myObject1]
[data.arr addObject:myObject2]
[data.arr addObject:myObject3]
[data saveDataToDisk];
}
At DisplayData.m, I want to check data.arr by
- (void) viewDidLoad {
Data *data = [[Data alloc] init];
[data loadDataFromDisk];
NSLog(#"length of array is %d",[data.arr count]);
}
On the console, I am getting
length of array is 1
I thought it should be 3 after all.
Please point out what I have just made a mistake in the middle of work if you have any clues about it.
So, I suspect that your "myObjects" are not NSCoding compliant. I just did this:
NSMutableArray *arr = [NSMutableArray arrayWithCapacity:3];
[arr addObject:#"Hello"];
[arr addObject:#" "];
[arr addObject:#"World"];
BOOL ret = [NSKeyedArchiver archiveRootObject:arr toFile:[self getPath]];
NSArray *arr2 = [NSKeyedUnarchiver unarchiveObjectWithFile:[self getPath]];
NSLog(#"count = %d", [arr2 count]);
And the results was "count = 3"
I feel like there's too much code here to do what you're looking for. I think all you need is:
[[NSUserDefaults standardUserDefaults] setObject:[NSKeyedArchiver archivedDataWithRootObject:dataClass] forKey:NSUserDefaultString];
[[NSUserDefaults standardUserDefaults] synchronize];
to save it.
And:
NSData *someData = [[NSUserDefaults standardUserDefaults] objectForKey:NSUserDefaultString];
if (settingsData != nil)
{
dataClass = [NSKeyedUnarchiver unarchiveObjectWithData:settingsData];
}
to retrieve it.

Potential leak of an object allocated in iphone

when i am analyze my project following code gives me leakage warning. is there any way to solve my memory leakage problem ?
warning :
Potential leak of an object allocated on line 38 and stored into 'addressBook'
Bellow is my code.
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
m_tableDataArray = [[[NSMutableArray alloc] init]autorelease];
NSMutableArray *listDate = [[[NSMutableArray alloc] init]autorelease];
ABAddressBookRef addressBook = ABAddressBookCreate();
NSArray *addresses = (NSArray *) ABAddressBookCopyArrayOfAllPeople(addressBook);
NSInteger addressesCount = [addresses count];
for (int i = 0; i < addressesCount; i++) {
ABRecordRef record = [addresses objectAtIndex:i];
NSString *firstName = (NSString *)ABRecordCopyValue(record, kABPersonFirstNameProperty);
NSString *lastName = (NSString *)ABRecordCopyValue(record, kABPersonLastNameProperty);
if(firstName != nil && lastName != nil){
NSString *contactFirstLast = [NSString stringWithFormat: #"%# %#", firstName, lastName];
[listDate addObject:contactFirstLast];
}
[firstName release];
[lastName release];
}
m_tableDataArray = [[NSArray arrayWithArray:listDate] retain];
[addresses release];
addresses = nil;
[m_mainTable reloadData];
}
Thanks in adv...
Once you have finished using addressBook you need to release it using:
CFRelease(addressBook);
This should probably be placed at the end of your viewWillAppear: method.
Updated: There are a few unnecessary arrays and steps in your version of viewWillAppear:. I have cleaned it up a bit and fixed a potential memory leak.
Note: I haven't actually run this so double-check that it works correctly.
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
// I assume m_tableDataArray is an instance variable. If so, if the
// view appears multiple times it will result in a leak unless we
// release pre-existing instances first.
[m_tableDataArray release], m_tableDataArray = nil;
m_tableDataArray = [[NSMutableArray alloc] init];
ABAddressBookRef addressBook = ABAddressBookCreate();
NSArray *addresses = (NSArray *)ABAddressBookCopyArrayOfAllPeople(addressBook);
for (ABRecordRef record in addresses) {
NSString *firstName = (NSString *)ABRecordCopyValue(record, kABPersonFirstNameProperty);
NSString *lastName = (NSString *)ABRecordCopyValue(record, kABPersonLastNameProperty);
if(firstName != nil && lastName != nil){
NSString *contactFirstLast = [NSString stringWithFormat: #"%# %#", firstName, lastName];
[m_tableDataArray addObject:contactFirstLast];
}
[firstName release];
[lastName release];
}
[addresses release], addresses = nil;
CFRelease(addressBook);
[m_mainTable reloadData];
}

giving nil while accessing the nsmutable array

i am parsing a json object and storing the song objects in songs, a nsmutable array.
while am displaying the image of the song i.e. while accessing the object from the array its giving all values nil in that object.
in the following code in setSongsScrollView method, in for loop while accessing the song object from songs array its showing nill in the debugger and crashing with error EXEBadacess.But the count of that array is giving correct.
can any body help me out please
- (void)viewWillAppear:(BOOL)animated{
[super viewDidLoad];
[self parsingTheStation];
[self load_images];
[self setSongsScrollView];
}
/*
// Implement loadView to create a view hierarchy programmatically, without using a nib.
- (void)loadView {
}
*/
- (void)parsingTheStation{
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http:...."]];
NSData *response = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
NSString *jsonString = [[NSString alloc] initWithData:response encoding:NSUTF8StringEncoding];
NSData *jsonData = [jsonString dataUsingEncoding:NSUTF32BigEndianStringEncoding];
NSDictionary *dictionary = [[CJSONDeserializer deserializer] deserializeAsDictionary:jsonData error:nil];
songs =[[NSMutableArray alloc]init];
NSArray *songObjects = [dictionary objectForKey:#"songs"];
for(NSDictionary *s in songObjects){
aSong = [[Song alloc] init];
aSong.artist = [s objectForKey:#"by"];
aSong.genre = [s objectForKey:#"genre"];
aSong.cover = [s objectForKey:#"cover"];
aSong.song_id = [s objectForKey:#"id"];
aSong.rank = [s objectForKey:#"rank"];
aSong.title = [s objectForKey:#"title"];
aSong.link = [s objectForKey:#"link"];
[songs addObject:aSong];
[aSong release];
}
NSLog(#"total number of songs is : %d",[songs count]);
}
-(void)setSongsScrollView {
songsContainer = [[UIScrollView alloc]init];
int songsCount = [self.songs count];
//totla no. of songs we get +4
int tSongs = songsCount+4;
int n = sqrt(tSongs);
int p = n,q = n;
int remSongs = tSongs-(n*n);
if(remSongs >= n){
q = q+(remSongs/n);
if((remSongs%n)>0)
q++;
}else q++;
for(int i=0;q>p;i++){
q--;
p++;
}
NSLog(#"total songs..%d",tSongs);
NSLog(#"total rows..%d",q);
NSLog(#"total columns..%d",p);
songsContainer.contentSize = CGSizeMake(120*q, 120*p);
int x =0, y=240, col=1;
for(int i=0;i<songsCount;i++){
CGRect imgFrame = CGRectMake(x, y, 118, 118);
NSLog(#"songs conunt ...%d",[songs count]);
Song *thesong = [[Song alloc]init];
thesong = [self.songs objectAtIndex:i];
NSString *filename = [NSString stringWithFormat:#"%#/%#", [LazyImageView dataPath], [thesong.cover lastPathComponent]];
UIImageView *tempImg = [[UIImageView alloc] initWithImage:[UIImage imageWithContentsOfFile:filename]];
tempImg.tag = i;
tempImg.frame = imgFrame;
[songsContainer addSubview:tempImg];
[tempImg release];
[thesong release];
y += 120;
if(y>=(120*p)){
NSLog(#"total y..%d",y);
col++;
x += 120;
if(col>=3)
y=0;
else
y=240;
}
}
NSLog(#"total y..%d",y);
NSLog(#"content size..%d,%d",120*q,120*p);
}
-(void)load_images{
for(int i=0;i<[songs count];i++){
Song *rsong = [[Song alloc]init];
rsong = [self.songs objectAtIndex:i];
lazyBigImg = [[LazyImageView alloc] init];
NSURL* url = [NSURL URLWithString:rsong.cover];
[lazyBigImg loadImageFromURL:url];
[lazyBigImg release];
[rsong release];
}
}
/*
// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
*/
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload {
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)dealloc {
[songs release];
[lazyBigImg release];
[onAirBtn release];
[chartsBtn release];
[dealsBtn release];
[searchBtn release];
[stNameLbl release];
[aSong release];
[songsContainer release];
[super dealloc];
}
#end
Marcel has basically got the right answer but I think a little more explanation is needed. Look at the following lines from setSongsScrollView:
Song *thesong = [[Song alloc]init];
The above line allocates a new Song that you own and assigns a reference to it to thesong
thesong = [self.songs objectAtIndex:i];
The above line replaces that reference with a new reference to a song from the array that you don't own. Remember that: you do not own the song referenced by thesong now. There are now no more references left to the object you just allocated, but you still own it. The object has therefore leaked.
NSString *filename = [NSString stringWithFormat:#"%#/%#", [LazyImageView dataPath], [thesong.cover lastPathComponent]];
Use stringByAppendingPathComponent: to build file paths, not stringWithFormat:.
UIImageView *tempImg = [[UIImageView alloc] initWithImage:[UIImage imageWithContentsOfFile:filename]];
tempImg.tag = i;
tempImg.frame = imgFrame;
[songsContainer addSubview:tempImg];
[tempImg release];
[thesong release];
The last line in the above sequence releases the object referenced by thesong. As noted above, you do not own that object. You must not release it, but you have anyway. This means that, at some point, may be now, maybe later, the object will be deallocated while something (probably the array) still thinks it has a valid reference. That's what causes the crash.
-(void)load_images{
for(int i=0;i<[songs count];i++){
Song *rsong = [[Song alloc]init];
rsong = [self.songs objectAtIndex:i];
lazyBigImg = [[LazyImageView alloc] init];
NSURL* url = [NSURL URLWithString:rsong.cover];
[lazyBigImg loadImageFromURL:url];
[lazyBigImg release];
[rsong release];
}
}
The above method contains exactly the same error.
You're creating a new Song instance (thesong), then assign this very instance to a song presumably already in the array. That makes no sense at all and is probably responsible for the memory error.
You shouldn't need to be creating new Songs if they are already in the array. Instead:
Song *thesong = [self.songs objectAtIndex:i];
Also look into using the Objective-C 2.0 for-each loop syntax.

unrecognized selector sent to instance... Source code link inside

I'm going crazy with this my little app... Please help me!!!
this is the source code of the app: Smoking.zip
It only saves a .dat file with an NSMutableArray.
Now, the first time you will launch the app, try to click the cigarette button sometimes: Everything should working fine.
Ok, now close the app, re-open it, and click again on the button. This time the app will crash with the "unrecognized selector sent to instance 0x5d18d60" error.
I was sure the problem was in saving the data, because when i commented the line "[theData writeToFile:dataFilePath atomically:YES];" in the "saveData" method the error disappeared.
Later i discovered that it appears again if i try to read the data from the NSMutableArray.
Please take a moment to check my project and help me, beacause i'm going crazy about that!!
Here's some code:
#import "SmokingAppDelegate.h"
#import "SmokingViewController.h"
#import "Cig.h"
#implementation SmokingAppDelegate
#synthesize window;
#synthesize viewController, dataFilePath, smokeArray;
#pragma mark -
#pragma mark Application lifecycle
- (id) init {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"a.dat"];
[self setDataFilePath:path];
NSFileManager *fileManager = [NSFileManager defaultManager];
if([fileManager fileExistsAtPath:dataFilePath]
) {
//open it and read it
NSLog(#"data file found. reading into memory");
smokeArray = [[NSMutableArray alloc] init];
NSMutableData *theData;
NSKeyedUnarchiver *decoder;
NSMutableArray *tempArray;
theData = [NSData dataWithContentsOfFile:dataFilePath];
decoder = [[NSKeyedUnarchiver alloc] initForReadingWithData:theData];
tempArray = [decoder decodeObjectForKey:#"smokeArray"];
[self setSmokeArray:tempArray];
[decoder finishDecoding];
[decoder release];
} else {
NSLog(#"no file found. creating empty array");
smokeArray = [[NSMutableArray alloc] init];
[smokeArray insertObject:[[NSNumber alloc] initWithInt:0] atIndex:0];
}
// [self logArrayContents];
return self;
}
- (void) logArrayContents {
for(int j = 1; j < [smokeArray count]; j++) {
int f = [[[smokeArray objectAtIndex:j] num] intValue];
NSLog(#"%i. - %d", j, f);
}
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
// Add the view controller's view to the window and display.
[window addSubview:viewController.view];
[window makeKeyAndVisible];
return YES;
}
-(void) saveData {
NSMutableData *theData;
NSKeyedArchiver *encoder;
theData = [NSMutableData data];
encoder = [[NSKeyedArchiver alloc] initForWritingWithMutableData:theData];
[encoder encodeObject:smokeArray forKey:#"smokeArray"];
[encoder finishEncoding];
[theData writeToFile:dataFilePath atomically:YES];
[encoder release];
NSLog(#"Saved");
}
#pragma mark -
#pragma mark Memory management
- (void)dealloc {
[viewController release];
[window release];
[dataFilePath release];
[smokeArray release];
[super dealloc];
}
#end
#import "SmokingViewController.h"
#import "SmokingAppDelegate.h"
#import "Cig.h"
#implementation SmokingViewController
#synthesize label;
- (void)viewDidLoad {
[super viewDidLoad];
SmokingAppDelegate *mainDelegate = (SmokingAppDelegate *)[[UIApplication sharedApplication] delegate];
//controlla se il giorno รจ lo stesso rispetto a quello dell'ultima sigaretta fumata
if ([mainDelegate.smokeArray count] > 1) {
Cig *oldCig = [mainDelegate.smokeArray lastObject];
NSArray *tempArray = [self quando];
if ( [[tempArray objectAtIndex:0] intValue]==[[oldCig.dat objectAtIndex:0] intValue]
&& [[tempArray objectAtIndex:1] intValue]==[[oldCig.dat objectAtIndex:1] intValue]
&& [[tempArray objectAtIndex:2] intValue]==[[oldCig.dat objectAtIndex:2] intValue]
) {
N = [oldCig.num intValue];
}
else {
N = 0;
}
[oldCig release];
[tempArray release];
}
//scrive quante sigarette si sono fumate oggi
label.text = [NSString stringWithFormat: #"Today you smoked %d cigarettes",N];
}
- (IBAction) smoke:(UIButton * ) button {
SmokingAppDelegate *mainDelegate = (SmokingAppDelegate *)[[UIApplication sharedApplication] delegate];
NSLog(#"L'array contiene %d sigarette", [mainDelegate.smokeArray count]-1);
N += 1;
[self addNewCigToArray];
[mainDelegate logArrayContents];
[mainDelegate saveData];
label.text = [NSString stringWithFormat: #"Today you smoked %d cigarettes",N];
}
- (void) addNewCigToArray {
//NSLog(#"new cigarette smoked");
SmokingAppDelegate *mainDelegate = (SmokingAppDelegate *)[[UIApplication sharedApplication] delegate];
Cig *newCig = [[Cig alloc] init];
[newCig setDat:[self quando]];
[newCig setNum:[[NSNumber alloc] initWithInt:N]];
[mainDelegate.smokeArray addObject:newCig];
[newCig release];
//[mainDelegate logArrayContents];
}
- (NSArray *) quando {
NSDateFormatter *timeFormat = [[NSDateFormatter alloc] init];
// 0 - Year
[timeFormat setDateFormat:#"YYYY"];
NSString *year = [timeFormat stringFromDate:[NSDate date]];
// 1 - Month
[timeFormat setDateFormat:#"MM"];
NSString *month = [timeFormat stringFromDate:[NSDate date]];
// 2 - Day
[timeFormat setDateFormat:#"dd"];
NSString *day = [timeFormat stringFromDate:[NSDate date]];
// 3 - Hour
[timeFormat setDateFormat:#"HH"];
NSString *hour = [timeFormat stringFromDate:[NSDate date]];
// 4 - Minute
[timeFormat setDateFormat:#"mm"];
NSString *min = [timeFormat stringFromDate:[NSDate date]];
// 5 - Second
[timeFormat setDateFormat:#"ss"];
NSString *sec = [timeFormat stringFromDate:[NSDate date]];
NSArray *newArray = [[NSArray alloc] initWithObjects:year,month,day,hour,min,sec,nil];
return newArray;
}
- (void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)dealloc {
[super dealloc];
}
#end
Okay, I am an iPhone newbie so take my suggestion in stride. You could try creating an NSData object, initializing it with theData, and then calling writeToFile on the new NSData object instead of the NSMutableData object.