I have an array of objects that I want to save as a file and reload back into my app. It's saving the file (with some data inside) but I can't get it to read back into a NSMutable Array.
The objects are models that conform to the NSCoding protocol:
#implementation myModel
#synthesize name;
#synthesize number;
-(void) encodeWithCoder: (NSCoder *) encoder
{
[encoder encodeObject:name forKey:#"name"];
[encoder encodeInteger:number forKey:#"number"];
}
-(id) initWithCoder: (NSCoder *) decoder
{
name = [decoder decodeObjectForKey:#"name"];
number = [decoder decodeIntegerForKey:#"number"];
return self;
}
#end
So I create an array of these objects, then I save it...
- (void) saveMyOptions {
// Figure out where we're going to save the app's data files
NSString *directoryPath = [NSString stringWithFormat:#"%#/Library/Application Support/MyAppDir/", NSHomeDirectory()]; // points to application data folder for user
// Figure out if that directory exists or not
BOOL isDir;
NSFileManager *fileManager = [[NSFileManager alloc] init];
[fileManager fileExistsAtPath:directoryPath isDirectory:&isDir];
// If the directory doesn't exist, create it
if (!isDir)
{
[fileManager createDirectoryAtPath:directoryPath withIntermediateDirectories:YES attributes:nil error:NULL];
}
// Assemble everything into an array of objects with options
NSMutableArray *savedPreferences = [[NSMutableArray alloc] init];
myModel *saveOptions = nil;
for (int i; i < [otherArray count]; i++)
{
saveOptions = [[myModel alloc] init];
[saveOptions setName:#"Some String"];
[saveOptions setNumber:i];
[savedPreferences addObject:saveOptions];
saveOptions = nil;
}
// Actually save those options into a file
NSData* saveData = [NSKeyedArchiver archivedDataWithRootObject:savedPreferences];
NSString *fileName = [NSString stringWithFormat:#"%#filename.stuff", directoryPath];
NSError *error = nil;
BOOL written = [saveData writeToFile:fileName options:0 error:&error];
if (!written)
{
NSLog(#"Error writing file: %#", [error localizedDescription]);
}
}
So now I try to load that data back into an array. This is where I think it's falling apart...
- (NSMutableArray *) loadOptions {
// Create file manager object
NSFileManager *fileManager = [[NSFileManager alloc] init];
NSData *saveData = nil;
// Find user directory path
NSString *directoryPath = [NSString stringWithFormat:#"%#/Library/Application Support/MyAppDir/", NSHomeDirectory()]; // points to application data folder for user
// Assign file name
NSString *fileName = [NSString stringWithFormat:#"%#filename.stuff", directoryPath];
// Create options array
NSMutableArray *myOptions = nil;
// If the file exists, fill the array with options
if ([fileManager fileExistsAtPath:fileName])
{
saveData = [NSData dataWithContentsOfFile:fileName];
myOptions = [NSKeyedUnarchiver unarchiveObjectWithData:saveData];
}
NSLog(#"%lu", [myOptions count]); // This ALWAYS reports 0!
NSLog(#"%lu", [saveData length]); // This reports a value of 236;
return myOptions;
}
Could someone point me in the direction of where I'm going wrong? I'm throughly confused :-(
Thanks in advance!
You are missing the super calls in your encodeWithCoder: and initWithCoder: methods, but that's just a guess. Why not use NSUserDefaults for saving preferences?
You might also want to make sure that your objects are retained is set using the synthesized setter.
- (void)encodeWithCoder:(NSCoder *)encoder {
[super encodeWithCoder:encoder];
[encoder encodeObject:name forKey:#"name"];
[encoder encodeInteger:number forKey:#"number"];
}
- (id)initWithCoder:(NSCoder *)decoder {
self = [super initWithCoder:decoder];
if (self) {
self.name = [decoder decodeObjectForKey:#"name"];
self.number = [decoder decodeIntegerForKey:#"number"];
}
return self;
}
For your info, NSKeyedArchiver also has a method you can use directly to operate on files:
+ (BOOL)archiveRootObject:(id)rootObject toFile:(NSString *)path
and NSKeyedUnarchiver:
+ (id)unarchiveObjectWithFile:(NSString *)path
Related
I'm trying to make an app with Objective C.
I'm trying to serialise an array existing out of objects and after wards deserialise it. Inside the object there are the methods
(id)initWithCoder:(NSCoder *)coder` and `encodeWithCoder:(NSCoder *)coder
But it seems the "rootObject" stays "nil" in the "loadDataFromDisk" -method
Here is my code :
#import "Alarm.h"
#implementation Alarm
#synthesize array = _array;
#synthesize time = _time;
#synthesize coder = _coder;
-(id)initWithCoder:(NSCoder *)coder
{
self = [super init];
if(self)
{
_array = [coder decodeObjectForKey:#"array"];
_time = [coder decodeObjectForKey:#"time"];
}
return self;
}
-(void)encodeWithCoder:(NSCoder *)coder
{
[coder encodeObject:self.array forKey:#"array"];
[coder encodeObject:self.time forKey:#"time"];
}
#end
My save and load methods :
-(void)saveDataToDisk
{
NSString * path = [self pathForDataFile];
NSLog(#"Writing alarms to '%#' %lu", path, (unsigned long)array.count);
NSMutableDictionary * rootObject;
rootObject = [NSMutableDictionary dictionary];
[rootObject setValue:array forKey:#"alarms"];
[NSKeyedArchiver archiveRootObject:rootObject toFile:path];
}
-(void)loadDataFromDisk
{
NSString *path = [self pathForDataFile];
NSDictionary *rootObject = [NSMutableDictionary dictionary];
rootObject = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
// "array" is an array with Objects of "Alarm"
array = [rootObject valueForKey:#"alarms"];
NSLog(#"Loaded from : %# %lu",path ,(unsigned long)array.count);
}
I hope anyone can help me out with this.
Thanks in advance.
You #synthized the array backing store (ivar) as _array. So you need to access the array as either _array or self.array. In saveDataToDisk and loadDataFromDisk it is accessed as array.
To test your array coding try something simple like this:
NSLog(#"array: %#", self.array);
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:self.array];
NSLog(#"data: %#", data);
NSArray *recovered = [NSKeyedUnarchiver unarchiveObjectWithData:data];
NSLog(#"recovered: %#", recovered);
Note: There is no need to wrap your array in a NSMutableDictionary.
When that works change it to the file based method calls.
Check that the filePath is valid.
Check that the file is created.
Check that the file contents are the same as in the above test code.
Note: There is no reason to wrap your array in a NSMutableDictionary.
I'm making a Cookbook application for the iPad and iPod, and I have an array of my Recipe class in my Cookbook class.
#interface Cookbook : NSObject<NSCoding>{
NSMutableArray* recipes;
}
That's in my Cookbook class, and in my recipe class I have this:
#interface Recipe : NSObject<NSCoding>{
NSString* name;
NSMutableArray* ingredients; //List of ingredients for the recipe
UIImage* recipePicture;
NSMutableArray* instructions;
unsigned int prepTime;//in seconds
NSDate* dateAdded;
}
(I actually have more variables in here, but I didn't want to flood this with an excessive amount)
My problem is basically in the save/load feature. I've asked a similar question before here:
How can I save an Objective-C object that's not a property list object or is there a better way for this than a property list?
This made me decide it'd be best to use NSCoding, and I've already implemented a method for it, too, in accordance with the way that was suggested with NSCoding.
My primary problem is that I can't get the recipes to be stored and successfully retrieved.
I've also had trouble getting the directory to my RecipeList.plist file to store the recipes in.
Any help would be greatly appreciated as this has been the reason I can't continue making this application.
In my Cookbook.m I have:
-(id) initWithCoder:(NSCoder *)aDecoder{
NSLog(#"Init With Coder - Cookbook");
if(self = [super init]){
recipes = [aDecoder decodeObjectForKey:#"recipes"];
}
return self;
}
In my Recipe.m:
-(id) initWithCoder:(NSCoder *)aDecoder{
if(self = [super init]){
name = [aDecoder decodeObjectForKey:#"name"];
ingredients = [aDecoder decodeObjectForKey:#"ingreds"];
recipePicture = [aDecoder decodeObjectForKey:#"recipePict"];
}
return self;
}
Once again, I have more variables in there, this is just for simplicity.
Also, this is my attempt at getting a file path to RecipeList.plist:
+(NSString*) filePath{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *path = [paths objectAtIndex:0];
path = [path stringByAppendingPathComponent:#"RecipeList.plist"];
NSLog(#"%#",path);
return path;
}
My attempt at a save method in my AppDelegate.m:
-(void) save:(NSString *)path cookbook:(Cookbook *)cookbook{
BOOL b = [[NSFileManager defaultManager] fileExistsAtPath:path];
NSLog(#"File exists: %i",b); //1 = exists, 0 = doesn't
NSMutableData* data = [[NSMutableData alloc] init];
if(data){ //If the data object was successfully initialized
NSKeyedArchiver* archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
if(archiver){
//Encode the recipe using the coder method defined in recipe.
[archiver encodeInt:1 forKey:#"Version"];
[archiver encodeObject:cookbook forKey:#"Cookbook"];
[archiver finishEncoding];
[data writeToFile:path atomically:YES];
}
}
}
and my load method:
-(Cookbook *) loadCookbook:(NSString *)path{
BOOL b = [[NSFileManager defaultManager] fileExistsAtPath:path];
NSLog(#"File exists: %i",b);
Cookbook* ret = nil;
NSData* data = [NSData dataWithContentsOfFile:path];
if(data){
NSKeyedUnarchiver* unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
int version = [unarchiver decodeIntForKey:#"Version"];
if(version == 1){
ret = (Cookbook*) [unarchiver decodeObjectForKey:#"Cookbook"];
}
[unarchiver finishDecoding];
}
return ret;
}
I also have a save and load method for my Recipe class very similar to this.
Once again, any help would be greatly appreciated, and thank you for taking the time to read through this.
EDIT: Here's the encodeWithCoder method in Recipe.m with a few variables omitted for the sake of brevity:
-(void) encodeWithCoder:(NSCoder *)aCoder{
NSLog(#"Encoding Recipe.");
[aCoder encodeObject:name forKey:#"name"];
[aCoder encodeObject:ingredients forKey:#"ingreds"];
[aCoder encodeObject:recipePicture forKey:#"recipePict"];
[aCoder encodeObject:instructions forKey:#"instructs"];
[aCoder encodeObject:category forKey:#"categ"];
[aCoder encodeObject:dateAdded forKey:#"dateAdd"];
[aCoder encodeInt:prepTime forKey:#"prepTime"];
}
and in Cookbook.m:
-(void) encodeWithCoder:(NSCoder *)aCoder{
NSLog(#"Encoding cookbook.");
[aCoder encodeObject:recipes forKey:#"recipes"];
}
I've "almost" copy-pasted your code and after some minor adjustments I've got it up and running.
To the Cookbook, I've added:
- (id)init {
if (self = [super init]) {
recipes = [NSMutableArray array];
}
return self;
}
and
- (void)addRecipe:(Recipe *)recipe {
[recipes addObject:recipe];
}
You might have those already? I assume you're instantiating the array somewhere?
Then using this test-routine, the cookbook is created, used and saved perfectly fine:
NSString *path = [self filePath];
Cookbook *cookbook = [self loadCookbook:path];
if (cookbook) {
NSLog(#"Loaded cookbook: %#", cookbook);
}
else {
cookbook = [[Cookbook alloc] init];
}
Recipe *recipe = [[Recipe alloc] init];
recipe->name = #"The name";
[cookbook addRecipe:recipe];
[self save:path cookbook:cookbook];
NSLog(#"Saved cookbook: %#", cookbook);
fyi: I've made the Recipe's instance variables #public for brevity; hopefully you're using accessors (#property).
Do you use Automatic or Manual reference counting?
I'm developing an iOS app using Core Data. And I have a Log entity with one-to-many relationships with Audio, Photo entities, and one-to-one relationship with Status entity. The log also has text, longitude, latitude properties. I can create the log, change its properties, add status entity, these changes would display right, until I quit the App. All the changes would disappear, and I was looking at the sqlite database, all these changes were never persisted in the database. In the database, the status object will just be created, but not linked to the log object.
But if I add an audio or photo object into the log.audioSet or log.photoSet, the changes I made to log, including the changes to text or status, will suddenly be saved into the database.
So it seems the changes are only maintained in the NSManagedObjectContext, until a related one_to_many entity is added and the [[LTLogStore sharedStore] saveChanges] will suddenly start to work.
I am using a singleton to manage the NSManagedObjectContext. Any ideas?
I would post some code if it's relevant. Thanks.
UPDATE: I'm not sure these code is enough. But basically everything works, and displays, it just doesn't save to the database. I'm using the mogenerator to set the text and latitude, but since everything is in the context. I am not sure this is the code you might need.
CODE:
#interface LTLogStore : NSObject{
}
+ (LTLogStore *)sharedStore;
- (void)removeItem:(Log *)p;
- (Log *)createItem;
- (BOOL)saveChanges;
#property(nonatomic, strong) NSFetchedResultsController *resultsController;
#property(nonatomic, strong) NSManagedObjectModel *model;
#property(nonatomic, strong) NSManagedObjectContext *context;
#end
#implementation LTLogStore
#synthesize resultsController;
#synthesize context, model;
+ (LTLogStore *)sharedStore
{
static LTLogStore *sharedStore = nil;
if(!sharedStore){
sharedStore = [[super allocWithZone:nil] init];
}
return sharedStore;
}
+ (id)allocWithZone:(NSZone *)zone
{
return [self sharedStore];
}
- (id)init
{
self = [super init];
if(self) {
model = [NSManagedObjectModel mergedModelFromBundles:nil];
NSPersistentStoreCoordinator *psc =
[[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];
// Where does the SQLite file go?
NSString *path = [self itemArchivePath];
NSURL *storeURL = [NSURL fileURLWithPath:path];
NSError *error = nil;
if (![psc addPersistentStoreWithType:NSSQLiteStoreType
configuration:nil
URL:storeURL
options:nil
error:&error]) {
[NSException raise:#"Open failed"
format:#"Reason: %#", [error localizedDescription]];
}
// Create the managed object context
context = [[NSManagedObjectContext alloc] init];
[context setPersistentStoreCoordinator:psc];
// The managed object context can manage undo, but we don't need it
[context setUndoManager:nil];
}
return self;
}
- (NSFetchedResultsController *)resultsController {
if (resultsController !=nil) {
return resultsController;
}
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *e = [[model entitiesByName] objectForKey:#"Log"];
[request setEntity:e];
NSSortDescriptor *sd = [NSSortDescriptor
sortDescriptorWithKey:#"created_at"
ascending:NO];
[request setSortDescriptors:[NSArray arrayWithObject:sd]];
[request setReturnsObjectsAsFaults:NO];
NSFetchedResultsController *fetchedResultsController = [[NSFetchedResultsController alloc]
initWithFetchRequest:request
managedObjectContext:context
sectionNameKeyPath:nil cacheName:#"Root"];
NSError *error;
BOOL success = [fetchedResultsController performFetch:&error];
if (!success) {
//handle the error
}
return fetchedResultsController;
}
- (NSString *)itemArchivePath
{
NSArray *documentDirectories =
NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES);
// Get one and only document directory from that list
NSString *documentDirectory = [documentDirectories objectAtIndex:0];
NSString *storePath = [documentDirectory stringByAppendingPathComponent:#"store.data"];
return storePath;
}
- (BOOL)saveChanges
{
NSError *err = nil;
BOOL successful = [context save:&err];
NSLog(#"Saving changes to the database");
if (!successful) {
NSLog(#"Error saving: %#", [err localizedDescription]);
}
return successful;
}
- (void)removeItem:(Log *)l
{
[context deleteObject:l];
[self saveChanges];
}
- (Log *)createItem
{
Log *p = [NSEntityDescription insertNewObjectForEntityForName:#"Log"
inManagedObjectContext:context];
[self saveChanges];
return p;
}
#end
#interface Log : _Log {
}
//these two are some custom convenience methods for location attributes, but it does the work of setting the longitude and latitude value in the log object, but calling the [[LTLogStore sharedStore] saveChanges] still won't save it into the database.
-(CLLocation*)location;
-(void)setLocation:(CLLocation*)location;
//this all works
-(Audio*)newAudio;
-(Audio*)newAudioWithPath:(NSString*)audioPath;
//after calling this method, even the log.text changes will be saved to the database.
-(void)addAudioWithPath:(NSString*)audioPath;
-(void)removeAudio:(Audio*)audio;
#end
#import "Log.h"
#import "Audio.h"
#import "LTLogStore.h"
#implementation Log
-(CLLocation*)location{
if (!self.longitude || !self.latitude) {
return nil;
}
CLLocation *l = [[CLLocation alloc] initWithLatitude:[self.latitude doubleValue] longitude:[self.longitude doubleValue]];
return l;
}
-(void)setLocation:(CLLocation*)location{
if (location==nil) {
self.latitude = nil;
self.longitude = nil;
}
self.latitude = [NSNumber numberWithDouble: location.coordinate.latitude];
self.longitude = [NSNumber numberWithDouble:location.coordinate.longitude];
[[LTLogStore sharedStore] saveChanges];
}
-(Audio*)newAudio{
Audio *a = [Audio new];
a.log = self;
return a;
}
-(Audio*)newAudioWithPath:(NSString*)audioPath{
Audio *new = [self newAudio];
[new setKey:audioPath];
return new;
}
-(void)addAudioWithPath:(NSString*)audioPath{
Audio *new = [self newAudio];
[new setKey:audioPath];
[[LTLogStore sharedStore] saveChanges];
}
-(void)removeAudio:(Audio*)audio{
[self.audiosSet removeObject:audio];
[[[LTLogStore sharedStore] context] deleteObject:audio];
[[LTLogStore sharedStore] saveChanges];
}
#end
UPDATE:
Problem solved, see answer.
UPDATE QUESTION: Why is my overriding causing the problem? Can someone explain the cause behind the magic of Core Data or maybe KVO behind scene?
Problem solved, I overrode the willChangeValueForKey method in the Log class, which caused the problem, I thought the code is irrelevant. But it IS:
- (void)willChangeValueForKey:(NSString *)key{
//I added the following line to fix my problem
[super willChangeValueForKey:key];
//this is the original line, I want to have this
//because I want to have a isBlank property
//so I can see if the user modified the log
_isBlank = false;
//I tried to also add the following line to be safe.
//turns out this line is not needed, and it will make the problem occur again
//[super didChangeValueForKey:key];
}
I have an APP (SDK 4.3) which saves images being attachmemnts for a waypoint on a google map.
The file save is pretty standard (afaik) UIImagePickerController code.
Rather than saving to the camera roll I was saving the image and then the thumbnail to a subfolder. I need that.
At seemingly random points with no errors being trapped at all and logged, the images will not save to the folder but instead over-write previously saved image files!
It looks for all the world like a FIFO pop going on.
It is seriously odd and I have even built a small test APP and fired it up as soon as the spookiness appeared...saving a series of camera images to the same folders but see the same effect. The images get over-written once the random magic file number is reached!
Random in the sense that after 7 saved images, the overwriting begins...even after a reboot of the phone to ensure memory leaks is not the issue. Wipe the APP and try again...
This time it will happen after 16 oR 23 image files saved.
I have gone to all sorts of extremes and cannot find the source of the issue.
In the small test APP, in the same method I save out to the camera roll as well. It will save there but overwrite in the folder. The file names are 10 character random generated alpha-numeric.
I am now leaning to understand this as a bug. I can always reproduce the error but not predictably. It arises randomly.
I would appreciate help as I am tearing my hair out.
Here is the code...
//tester.h
#import <UIKit/UIKit.h>
#interface tester : UIViewController <UINavigationControllerDelegate, UIImagePickerControllerDelegate>
{
UIImagePickerController *imgPicker;
IBOutlet UIButton *pressit;
IBOutlet UIButton *seeya;
UIActivityIndicatorView *activity;
}
#property (retain )UIImagePickerController *imgPicker;
#property (nonatomic,retain)IBOutlet UIButton *pressit;
#property (nonatomic,retain)IBOutlet UIButton *seeya;
#property (nonatomic,retain)UIActivityIndicatorView *activity;
-(NSString *) genRandStringLength:(int) len ;
-(void)saveImagesFromPickerInTheBackgroundUsingImage:(UIImage *)img;
-(NSArray *)buildFilePaths;
- (IBAction)snapShots:(UIButton *)button;
-(IBAction)byebye:(id)sender;
#end
//=====================
//tester.m
#import "tester.h"
#import "MultiMediaUtilities.h"
#implementation tester
#synthesize imgPicker;
#synthesize pressit,seeya,activity;
//Image size constants
#define MAX_THUMBNAIL_RES_SIZE 103
#define MAX_IMAGE_RES_SIZE 640
- (IBAction)snapShots:(UIButton *)button
{
if (!imgPicker) imgPicker = [[UIImagePickerController alloc]init];
imgPicker.sourceType = UIImagePickerControllerSourceTypeCamera;
imgPicker.delegate = self;
[self presentModalViewController:imgPicker animated:YES];
}
- (void) imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
UIImage *memoImage = [[MultiMediaUtilities scaleAndRotateImage:[info objectForKey:#"UIImagePickerControllerOriginalImage"] toResolution:MAX_IMAGE_RES_SIZE ]retain];
UIImageWriteToSavedPhotosAlbum(memoImage, self, #selector(image:didFinishSavingWithError:contextInfo:), nil);
[self saveImagesFromPickerInTheBackgroundUsingImage:memoImage];
// Dismiss the camera
[self dismissModalViewControllerAnimated:YES];
}
//builds paths to files in system with components
-(NSArray *)buildFilePaths
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *docsPath = [documentsDirectory stringByAppendingPathComponent:#"imagesfolder"];
NSString *fullDocsPath = [docsPath stringByAppendingPathComponent:#"assets"];
NSString *fullThumbsPath = [fullDocsPath stringByAppendingPathComponent:#"thumbs"];
NSArray * retArray = [NSArray arrayWithObjects:fullDocsPath,fullThumbsPath,nil];
return retArray;
}
-(void)saveImagesFromPickerInTheBackgroundUsingImage:(UIImage *)img
{
#try
{
NSFileManager *NSFm = [NSFileManager defaultManager];
NSArray *pathsArray = [NSArray arrayWithArray:[self buildFilePaths]];
NSString *fullDocsPath = [NSString stringWithFormat:#"%#", (NSString *)[pathsArray objectAtIndex:0]];
NSString *fullThumbsPath = [NSString stringWithFormat:#"%#", (NSString *)[pathsArray objectAtIndex:1]];
//Ensure Folders exist
BOOL isDir=YES;
NSError *error;
if(![NSFm fileExistsAtPath:fullDocsPath isDirectory:&isDir])
if(![NSFm createDirectoryAtPath:fullDocsPath withIntermediateDirectories:YES attributes:nil error:&error])
NSLog(#"Error: Create Images folder failed");
//create thumbs folder too
if(![NSFm fileExistsAtPath:fullThumbsPath isDirectory:&isDir])
if(![NSFm createDirectoryAtPath:fullThumbsPath withIntermediateDirectories:YES attributes:nil error:&error])
NSLog(#"Error: Create Thumbs folder failed");
//build the filenames & paths
NSString *newImageName= [NSString stringWithFormat:#"%#.png", [self genRandStringLength:10]];
NSString *imagePath = [[fullDocsPath stringByAppendingPathComponent:newImageName]retain];
NSLog(#"SavingIMage ImagePath = %#",imagePath);
NSString *thumbPath = [[fullThumbsPath stringByAppendingPathComponent:newImageName]retain];
NSLog(#"SavingIMage thumbPAth = %#",thumbPath);
//Write the files out
NSData *imgData = UIImagePNGRepresentation(img);
[imgData writeToFile:imagePath options:NSDataWritingAtomic error:&error];
if (!error) {
NSLog(#"Error writing image %#",error.description);
}
NSData *thumbData = UIImagePNGRepresentation(img);
[thumbData writeToFile:thumbPath options:NSDataWritingAtomic error:&error];
if (!error) {
NSLog(#"Error writing thumb %#",error.description);
}
}
#catch (NSException * e)
{
NSLog(#"Exception: %#", e);
}
}
-(NSString *) genRandStringLength:(int) len
{
NSString *letters = #"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
NSMutableString *randomString = [NSMutableString stringWithCapacity: len];
for (int i=0; i<len; i++)
{
[randomString appendFormat: #"%c", [letters characterAtIndex: rand()%[letters length]]];
}
return randomString;
}
- (void)image:(UIImage*)image didFinishSavingWithError:(NSError *)error contextInfo:(NSDictionary*)info {
NSString *message;
NSString *title;
if (!error)
{
title = #"Camera...";
message = #"Image saved!...Just as well.";
}
else
{
title = #"Error";
message = [error description];
}
UIAlertView *alert = [[UIAlertView alloc]
initWithTitle:title
message:message
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
[alert release];
if (image !=NULL){
[image release];
image=nil;
}
if(info !=NULL)
{
[info release];
info=nil;
}
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
-(void)dealloc
{
[imgPicker release];
[pressit release];
[seeya release];
[activity release];
[super dealloc];
}
#end
Even seeded, this is an inappropriate use of random numbers.
Three approaches:
Use an incremented sequence number. (1, 2, 3, etc.)
Use a UUID from [[NSProcessInfo processInfo] globallyUniqueString]
Use a filename constructed from the date & time.
As Mats said, if you don't initialize your random number generator with srand, rand() will behave strangely and don't expect it to generate random numbers. This can cause the same filenames you experience.
Hi, I am trying to save an object from a class I have created. It is called shot and Contains 5 variables I wish to save. Here is the .h file--- It cut off NSCoding and NSMutableCopying Protocals and my imports, but they are there.
#import <Foundation/Foundation.h>
#interface Shot : UIButton <NSCoding, NSMutableCopying> {
int x;
int y;
int location;
int quarter;
bool made;
int miss;
int make;
}
#property()int x;
#property()int y;
#property()int location;
#property()int quarter;
#property()bool made;
-(void)set_x:(int)my_x set_y:(int)my_y set_quarter:(int)my_quarter set_made:(bool)my_made set_location:(int)my_location;
-(void)set_miss_AND_set_make;
#end
**Here are the methods I made to save the data in my .m file---**
-(void)encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeInt:x forKey:#"x"];
[aCoder encodeInt:y forKey:#"y"];
[aCoder encodeInt:location forKey:#"location"];
[aCoder encodeInt:quarter forKey:#"quarter"];
[aCoder encodeBool:made forKey:#"made"];
}
-(id)initWithCoder:(NSCoder *)aDecoder {
if (self = [super init]) {
x = [aDecoder decodeIntForKey:#"x"];
y = [aDecoder decodeIntForKey:#"y"];
location = [aDecoder decodeIntForKey:#"location"];
quarter = [aDecoder decodeIntForKey:#"quarter"];
made = [aDecoder decodeBoolForKey:#"made"];
}
return self;
}
-(id)mutableCopyWithZone:(NSZone *)zone {
Shot *newShot = [[Shot allocWithZone:zone]init];
[newShot set_x:x set_y:y set_quarter:quarter set_made:made set_location:location];
return newShot;
}
I can't seem to get my data to saved when I use these methods
-(NSString *)getPath {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentFolder = [paths objectAtIndex:0];
NSFileManager *fm = [[NSFileManager alloc]init];
if ([fm fileExistsAtPath:documentFolder] == NO) {
[fm createDirectoryAtPath:documentFolder withIntermediateDirectories:YES attributes:nil error:nil];
}
return [documentFolder stringByAppendingFormat:#"iStatTrackInfo.archive"];
}
-(void)saveData {
NSString *path = [self getPath];
[NSKeyedArchiver archiveRootObject:shotArray toFile:path];
}
-(void)loadData {
NSString *path = [self getPath];
NSFileManager *fm = [[NSFileManager alloc]init];
if ([fm fileExistsAtPath:path] == YES) {
shotArray = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
return;
}
NSEnumerator *enumOne = [shotArray objectEnumerator];
Shot *shotObject = [[Shot alloc]init];
while (shotObject = [enumOne nextObject]) {
Shot *shotShot = [[Shot alloc]init];
shotShot = [shotObject mutableCopy];
shotShot.frame = CGRectMake(0, 0, 50, 50);
shotShot.backgroundColor = [UIColor whiteColor];
[self addSubview:shotShot];
}
}
I have set the save and load methods up to buttons, but my data still won't save. Please help!!!
I have finally solved the problem. Other than the above help, .archive isn't a file type. You can't save a .archive, you can only save a .arch or a .plist That was my main problem.
First, you are leaking memory like a sieve. Lots of alloc/init & mutableCopy, no releases. I'd suggest reading the memory management guide.
Next, your method names are not following the Cocoa conventions. These:
-(void)set_x:(int)my_x set_y:(int)my_y set_quarter:(int)my_quarter set_made:(bool)my_made set_location:(int)my_location;
-(void)set_miss_AND_set_make;
Should be something like:
-(void) resetMissAndMake; // or just missAndMake or activateMissAndMake
-(void) setX:(NSInteger)xValue y:(NSInteger)yValue quarter:(NSInteger)quarterValue location:(NSInteger)aLocation;
Also, getPath should just be path. Methods prefixed with get are both very rare and have a very specific meaning.
This is also nonsense:
Shot *shotObject = [[Shot alloc]init];
while (shotObject = [enumOne nextObject]) {
Shot *shotShot = [[Shot alloc]init];
shotShot = [shotObject mutableCopy];
There is no need for either of the [[Shot alloc]init] calls (those are both leaks).
Finally, your encoding methods are implemented incorrectly. As the documentation states, you need to call super as appropriate.
Specifically, your encodeWithCoder: must invoke super's implementation of same and initWithCoder: should call super's initWithCoder:, not init.