Saving Arrays Between Classes - iphone

Currently attempting to save an array that is populated according to which cells in a UITableView are chosen and saving this array in an instance of a seperate object. I am getting the array to populate just fine, however, my save method, which is an IBAction that is invoked by clicking on a Bar Button doesn't seem to be working. Here is some code:
-(IBAction)saveWorkout:(id)sender {
Workouts *new = [[Workouts alloc] init];
[new addNewWorkout:customWorkout];
[customWorkout removeAllObjects];
}
This code is from the first class.
And here is the code for my addNewWorkouts method in the Workouts class:
-(void)addNewWorkout:(NSMutableArray*)array {
NSMutableArray *temp = [[NSMutableArray alloc] init];
temp = array;
self.workoutList = temp;
[temp release];
}
Here is my "Workout.h"
#import <Foundation/Foundation.h>
#interface Workouts : NSObject {
NSString *workoutName;
NSMutableArray *workoutList;
NSString *description;
int *reps;
int *weights;
int *sets;
}
#property (nonatomic, retain) NSString *workoutName;
#property (nonatomic, retain ) NSString *description;
#property (nonatomic, retain) NSMutableArray *workoutList;
-(void)addNewWorkout:(NSMutableArray*)array;
#end
Before running this code, I get a Warning from Xcode saying that 'Workouts may not respond to 'addNewWorkouts.'
Anyone know what is causing this error? Once I build & run, I click on the Save button and the app crashes with a unrecognized selector sent to instance 0x3b04410 error.

You call [new addNewWorkouts:customWorkout]
when the method's selector is addNewWorkout: (note that there is no plural in the method name)
This will make a bad method call and result in a crash.
Also, there is a problem with the memory management of the addNewWorkout method.
1- NSMutableArray *temp = [[NSMutableArray alloc] init];
2- temp = array;
3- self.workoutList = temp;
4- [temp release];
You allocate a new NSMutableArray on line 1, then lose its reference on line 2 when you replace its pointer by 'array'. The allocation you just made is lost and the program will leak.
Then, on line 4, you send a release message to 'temp' which actually points to 'array', resulting in the release of the parameter that you received and not the temporary object.
Is there a reason whny you create a temporary array? You can just assign the property and make the property copy or retain it, depending on your needs.

Related

Error in accessing global declared NSArray values in class methods

I have problem in fetching the element from array,
I have declared NSArray *array globally, also tried through myfile.h file property, but I'm not able to access array element in my view controller setCode method.
NSArray *array;
//this is global array also i tried in my.h file
//#property(nonatomic, retain) NSArra *array;
//and by accessing it #synthesize array; also not works for me.
-(void)viewDidLoad
{
array = [NSArray arrayWithObjects:#"sssss", #"hjjjjj", #"kkkkkk"];
[self setCode];
}
-(void)setCode
{
NSString *code = [array objectAtIndex:0];
NSLog(#"code %#",code);
}
#end
It throwing some errors:
2013-08-05 16:25:28.429 test_project_ios_31st_july[2409:c07] -[__NSMallocBlock__ objectAtIndex:]: unrecognized selector sent to instance 0x9c82350
2013-08-05 16:25:28.430 test_project_ios_31st_july[2409:c07] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSMallocBlock__ objectAtIndex:]: unrecognized selector sent to instance 0x9c82350'
*** First throw call stack:
(0x23fb012 0x1c98e7e 0x24864bd 0x23eabbc 0x23ea94e 0x283f 0x17594f9 0x24550c5 0x23afefa 0x168dbb2 0x16a0e6b 0xdb9f 0x16d2417 0x16ebb24 0x16a0d60 0x174da8a 0x43ac2 0x16d2417 0x16ebb24 0x16a0d60 0x174da8a 0x464d5 0x17594f9 0x24550c5 0x23afefa 0x16a0a0c 0xa3d0e6 0x206353f 0x2075014 0x20657d5 0x23a1af5 0x23a0f44 0x23a0e1b 0x2ad87e3 0x2ad8668 0xbdcffc 0x2192 0x20c5)
libc++abi.dylib: terminate called throwing an exception
Please try following code:
NSArray *array;
-(void)viewDidLoad
{
array = [NSArray arrayWithObjects:#"sssss", #"hjjjjj", #"kkkkkk"];
[array retain];
[self setCode];
}
-(void)setCode
{
NSString *code = [array objectAtIndex:0];
NSLog(#"code %#",code);
[array release];
}
#end
You are declaring another object from the array inside your viewDidLoad, so your global array object will never get allocated, you need to remove the declaration as below:
-(void)viewDidLoad
{
array = [NSArray arrayWithObjects:#"sssss", #"hjjjjj", #"kkkkkk"];
int i;
[self setCode];
}
And by the way the setCode doesn't do anything rather than printing!, you can change its name to printCode.
Add
#interface PeopelListViewController : UIViewController
{
NSArray *array;
}
//in my.h file
//#property(nonatomic, retain) NSArra *array;
//and #synthesize array;
just you need to Put nil at the end of your NSArray.
such like
self.array = [NSArray arrayWithObjects:#"sssss", #"hjjjjj", #"kkkkkk", nil];
first you declare an array in implementation (.m file) like this
#implementation : myviewcontroller {
NSArray *array;
}
then initialize it like
array = [NSArray arrayWithObjects:#"sssss", #"hjjjjj", #"kkkkkk", nil];
it must work
What you did is to create a reference - local, here in this method.
-(void)viewDidLoad
But in your -(void)setCode method, you are accessing your global variable .
The global variable is not yet allocated a memory, neither initialized hence the error.
So you need to understand the scope of variables. Local scope preceeds global scope. Anyways you are working with two different array objects.
Change this in your viewDidload implementation to make it work:
// No NSArray at front that will make it an another array object
array = [NSArray arrayWithObjects:#"sssss", #"hjjjjj", #"kkkkkk"];

objective-c beginner: getter setter prob and EXC_BAD_ACCESS error

Iam getting an EXC_BAD_ACCESS all the time and I cannot figure out why...
Simple task:
The Parser Class pases XML with touchXML in an NSMutableArray called listArray.
In the Method grabCountry I can access the listArray and listArray.count works well.
Now I need the listArray.count in another Class the MasterViewController.
But Im getting an EXC_BAD_ACCESS error all the time.
Please help!
Here is the code snipplet:
Parser.h
#import <Foundation/Foundation.h>
#interface Parser : NSObject
#property (strong, retain) NSMutableArray *listArray;
#property (strong, retain) NSURL *url;
-(void) grabCountry:(NSString *)xmlPath;
#end
Parser.m
#import "Parser.h"
#import "TouchXML.h"
#implementation Parser
#synthesize listArray;
#synthesize url;
-(void) grabCountry:(NSString *)xmlPath {
// Initialize the List MutableArray that we declared in the header
listArray = [[NSMutableArray alloc] init];
// Convert the supplied URL string into a usable URL object
url = [NSURL URLWithString: xmlPath];
//XML stuff deleted
// Add the blogItem to the global blogEntries Array so that the view can access it.
[listArray addObject:[xmlItem copy]];
//works fine
NSLog(#"Amount: %i",listArray.count);
}
#end
MasterViewController.h
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
#import "TouchXML.h"
#import "Parser.h"
#class Parser;
#interface MasterViewController : UITableViewController{
Parser *theParser;
}
#end
MasterViewControlelr.m
- (void)viewDidLoad
{
NSString *xmlPath = #"http://url/to/xml.xml";
theParser = [[Parser alloc] init];
//Starts the parser
[theParser grabCountry:xmlPath];
//Here I want to access the Array count, but getting an BAD ACCESS error
NSLog(#"Amount %#",[theParser.listArray count]);
[super viewDidLoad];
}
Can anyone explain me what the problem here is?
Thanks!
Internally, each #property has a corresponding instance variable.
In your -grabCountry method, you are directly accessing the instance variable in the statement listArray = [[NSMutableArray alloc] init]; (same with url = [NSURL URLWithString: xmlPath];), instead of the #property's setter method, causing the NSMutableArray that you alloc-init'd to not be retained by the property. To invoke the #property's setter method, you should call
NSMutableArray *temp = [[NSMutableArray alloc] init];
self.listArray = temp; // or [self setListArray:temp];
[temp release];
If you want to have Xcode show an error when you are directly accessing the instance variable of an #property, you can have #synthesize listArray = _listArray, which changes the name of the instance variable to _listArray.
Generally, if there is an alloc-init, there must be a corresponding release (except if using Automatic Reference Counting).
Also, in the [listArray addObject:[xmlItem copy]]; statement, the call to copy is not needed, as NSArrays retain every object that is added to them. Calling copy also increases the retain count, which is another leak. Instead, you should just have [self.listArray addObject:xmlItem];
You are getting EXC_BAD_ACCESS because in NSLog(#"Amount %#",[theParser.listArray count]);, you are using %# format specifier, which is for NSStrings. You want to print the array's count, an integer, so you should be using %d or %i.

How do I removeAllObjects on my NSMutableArray that is part of another object?

I have an object defined like this:
Scores.h:
#interface Scores : NSObject {
NSString *sentenceKey;
NSMutableArray *scorrectAnswers;
}
#property (nonatomic, copy) NSString *sentenceKey;
#property (nonatomic, copy) NSMutableArray *scorrectAnswers;
+ (id)addScore:(NSString *)senKey;
- (id)initWithSentenceKey:(NSString *)sKey
scorrectAnswers:(NSMutableArray *)scorrectAs;
- (id)initWithSentenceKey:(NSString *)sKey;
- (void)removeArrayObjects;
Score.m:
#import "Scores.h"
#implementation Scores
#synthesize sentenceKey, scorrectAnswers;
+ (id)addScore:(NSString *)senKey
{
Scores *newScore = [[self alloc] initWithSentenceKey:senKey
scorrectAnswers:[NSMutableArray new]];
return [newScore autorelease];}
I'm trying to removeAllObjects on my mutable array with this method:
- (void)removeArrayObjects;{
[scorrectAnswers removeAllObjects];}
...which I call from another program like this:
for (Scores *sScore in scores)
{
[sScore removeArrayObjects];
}
... and I get this error when I run it:
-[__NSArrayI removeAllObjects]: unrecognized selector sent to instance 0x53412d0
Can anyone tell me what I'm doing wrong here? Thanks.
You are not dealing with an NSMutableArray as the error indicates you have an immutable NSArray.
This question may be your answer NSMutableArray addObject: -[__NSArrayI addObject:]: unrecognized selector sent to instance
Basically the copy you used when defining your #property will cause the setter to be generated using
scorrectAnswers = [newMutableArray copy];
which returns an immutable NSArray.
You can re-implement this method and change the previous line for:
scorrectAnswers = [newMutableArray mutableCopy];
or use retain instead of copy
This can also occur when getting data from a plist
If you are using a plist it will return an NSArray even if you save an NSMutableArray it will be cast. So when retrieving you will need to do something like:
scorrectAnswers = [[NSMutableArray alloc] initWithArray:[userDefault objectForKey:#"my_array_key"]]
It doesn't look like like the memory pointed to by scorrectAnswers is actually pointing to an NSMutableArray. Where and how do you initialize that variable? If you are setting the ivar directly with an autoreleased object, like:
scorrectAnswers = [NSMutableArray arrayWithCapacity:10];
then the autoreleased array will be destroyed, since you're not retaining it (or copying it). If that memory gets reallocated to point to another object, you'll see an error like the one you're getting, with an unexpected type. If the memory has not been reallocated, you'll get an EXC_BAD_ACCESS error.
Similar to what was mentioned above, I had an NSMutableArray which was being re-allocated somewhere in my code as an NSArray. Once I modified that to correctly be an NSMutableArray, it resolved my problem.
I would suggest doing a quick search to ensure that you have not reallocated the array somewhere in your project and modify accordingly.

How to save a NSMutableArray (containing other arrays) to file

This has been asked before and people have given very good instructions on how to do this, e.g. here.
However, I was wondering if I really need to work with NSCoder if I simply wanted to save one NSMutableArray (containing various instances of another NSMutableArray) to a file? I tried this but only got an error message:
-(void)saveLibraryDat {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0]; // Get documents directory
NSString *filePath = [documentsDirectory stringByAppendingPathComponent:#"myLibrary.dat"];
NSError *error;
[myLibrary writeToFile:filePath atomically:YES];
if (error) {
NSLog(#"There was an error saving myLibrary.dat: %#", error);
}
}
My error message:
2011-05-13 22:00:47.840 MoleNotes[15437:207] There was an error saving myLibrary.dat: (
1,
2
)
So I guess I have to work with NSCoder, right? If so, I was wondering how to go about this. People have explained how to do this with a class, but in my case, I have a NSMutableArray (myLibrary) which contains various instances of a class. Will I have to implement the NSCoder in this class and the NSMutableArray?
I alloc my library like this:
myLibrary = [[NSMutableArray alloc] init];
And then add instances of a class called NoteBook.m like this:
NoteBook *newNoteBook = [[NoteBook alloc] init];
newNoteBook.titleName = #"Notes"; // etc.
[myLibrary addObject:newNoteBook];
So where exactly do I put the NSCoder commands? Only into my NoteBook.m class? Will this automatically take care of myLibrary?
Thanks for any suggestions.
EDIT:
So I've updated my code, but I guess the big problem is that my NSMutableArray myLibrary contains several instances of a custom class I've set up (called notebook). I have set up NSCoding for this class (and all its variables) so that I can save it and load it.
Now my app works totally fine if I create the NSMutableArray in the app (i.e. when the app is started for the very first time, no file exists), instead of loading it from disk:
-(void) setupLibrary {
myLibrary = [[NSMutableArray alloc] init];
NoteBook *newNoteBook = [[NoteBook alloc] init];
newNoteBook.titleName = #"Notes";
/...
If I load it from disk, it works fine as well:
-(void)loadLibraryDat {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0]; // Get documents directory
NSString *filePath = [documentsDirectory stringByAppendingPathComponent:#"myLibrary.dat"];
myLibrary = [[NSMutableArray alloc] init];
myLibrary = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
if (!myLibrary) {
// if it couldn't be loaded from disk create a new one
NSLog(#"myLibrary.dat empty... set up new one");
[self setupLibrary];
} else { NSLog(#"Loading myLibrary.dat successful."); }
}
If I log everything which is contained in my library after loading it, everything is still fine. E.g. the following works totally fine:
NSLog(#"%#", [[self.myLibrary objectAtIndex:0] titleName]);
The big problem is, however, if any other method tries to access myLibrary. For instance, if I call the very same log command from another method, the app will crash and I get this error message:
[NSCFString objectAtIndex:]: unrecognized selector sent to instance 0x4b38510
2011-05-14 14:09:10.490 Notes[17091:207] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSCFString objectAtIndex:]: unrecognized selector sent to instance 0x4b38510'
This sounds to me as if myLibrary has become deallocated somehow, but I can't see why. How could this have happened? I have the feeling that I did something wrong in my NSCoding set up... because if I simply create myLibrary in code, everything works like wonderfully. It's only if I load it from the disk, that the app will crash.
Here is the class setup:
#import <Foundation/Foundation.h>
#interface NoteBook : NSObject <NSCoding> {
NSString *titleName;
NSString *fileName;
NSMutableArray *tabTitles;
NSMutableArray *tabColours;
NSMutableArray *tabReference;
}
#property (nonatomic, retain) NSString *titleName;
#property (nonatomic, retain) NSString *fileName;
#property (nonatomic, retain) NSMutableArray *tabTitles;
#property (nonatomic, retain) NSMutableArray *tabColours;
#property (nonatomic, retain) NSMutableArray *tabReference;
-(id)initWithCoder:(NSCoder *)aDecoder;
-(void)encodeWithCoder:(NSCoder *)aCoder;
#end
//
// NoteBook.m
#import "NoteBook.h"
#implementation NoteBook
#synthesize titleName, fileName, tabTitles, tabColours, tabReference;
- (id)initWithCoder:(NSCoder *)aDecoder {
self = [super init];
if (self) {
self.titleName = [aDecoder decodeObjectForKey:#"titleName"];
self.fileName = [aDecoder decodeObjectForKey:#"fileName"];
self.tabTitles = [aDecoder decodeObjectForKey:#"tabTitles"];
self.tabColours = [aDecoder decodeObjectForKey:#"tabColours"];
self.tabReference = [aDecoder decodeObjectForKey:#"tabReference"];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeObject:titleName forKey:#"titleName"];
[aCoder encodeObject:fileName forKey:#"fileName"];
[aCoder encodeObject:tabTitles forKey:#"tabTitles"];
[aCoder encodeObject:tabColours forKey:#"tabColours"];
[aCoder encodeObject:tabReference forKey:#"tabReference"];
}
#end
EDIT:
I think I've solved it... I forgot a little 'self'... which messed it all up and deallocated myLibrary:
self.myLibrary = [NSKeyedUnarchiver
unarchiveObjectWithFile:filePath];
if (self.myLibrary == nil) {
NSLog(#"myLibrary.dat empty... set up new one");
[self setupLibrary];
} else { NSLog(#"Loading myLibrary.dat successful."); }
Your code is busted. The "error" variable is uninitialized and never set, so when you check it, you're just seeing random garbage data. If you want to know whether the write was successful, check the return value of writeToFile:atomically:. It will be YES if the write succeeded and NO if it didn't.
However, NSArray's writeTo… methods are for creating plists. If non-property-list objects are in your array, that method isn't appropriate, and an archiver is what you want. Just do something like [[NSKeyedArchiver archivedDataWithRootObject:myLibrary] writeToFile:writeToFile:filePath atomically:YES].
To make your objects conform to NSCoding correctly, just have them implement initWithCoder: and encodeWithCoder:, and in those methods, use NSCoder's storage methods to store the object's instance variables (and the retrieval methods to get them back out).
NSCoder is a protocol that your class must conform to in order to be archived to data/file. Works something like Serealizabe in Java.
Add conformance to the class header like this:
#interface NoteBook : NSObject <NSCoder> { // …
And then you must implement two methods:
-(id)initWithCoder:(NSCoder)decoder;
{
self = [super initWithCoder:decoder];
if (self) {
_someIvar = [decoder decodeObjectForKey:#"someKey"];
// And more init as needed…
}
return self;
}
-(void)encodeWithCoder:(NSCoder)coder;
{
[super encodeWithCoder:coder];
[coder encodeObject:_someIvar forKey#"someKey"];
/// Etc…
}
I would also advice against using -[NSArray writeToFile:atomically:] since in work with property list compliant objects only, not coding compliant classes. The property list object are NSString, NSData, NSArray, or NSDictionary, NSDate, and NSNumber. The list can not be extended.
Instead use NSKeyedArchiver/NSKeyedUnarchiver. Almost as simple to use:
if (![NSKeyedArchive archiveRootObject:yourArrat toFile:path]) {
// It failed.
}

Need to store array of an array of class objects in View Controller

An iPhone question for you guys! I have an NSURLConnection that downloads XML from a server and processes it into an array which is a part of another array. I have no way of knowing how many objects I will need, so I cannot allocate an NSArray beforehand. My question is:
Would it be better to create the parent array as an NSArray at the class level and allocate it after I store the data in a temporary NSMutableArray or just make the NSMutableArray at class level? It is worth noting that I do not need to modify the array other than to release it at the end of the program run.
I don't think it really matters.
I'm reading the Beginning iPhone 3 Development book at the moment, and usually loading the data is done like this:
You'd have an NSArray property :
#interface
{
...
NSArray *listOfObjects;
...
}
#property (nonatomic, retain) NSArray *listObObjects;
...
Then you create an NSMutableArray, load your data and set the property:
NSMutableArray *array = [[NSMutableArray alloc] init]; // ?
// load the XML into array here
...
self.listOfObjects = array;
[array release];
listOfObjects would then be treated as an NSArray (immutable) although it actually would be an NSMutableArray.
I think what you probably want to do is create some Classes that match what you are representing in your xml. For example if you xml looks something like this:
<peopleList>
<person>
<name>Joe</name>
<possession>Shovel</possession>
</person>
<person>
<name>Sam</name>
<possession>Shovel</possession>
<possession>Fork</possession>
<possession>Backpack</possession>
</person>
</peopleList>
You should have a PeopleList Class and a Person Class. An object instantiated from the PeopleList Class has your first array that contains one or more Person objects. The Person objects, in turn, also have arrays to hold the possesions (which in this case are strings - although if needed they could be Possesion objects) In this case, to help the example the Person Class also has another property: 'name' which is also a String.
For example:
#interface PeopleList {
NSMutableArray *persons; // An array to store the Person objects
}
#property (nonatomic, retain) NSMutableArray *persons;
#end
#interface Person {
NSString *name;
NSMutableArray *possesions; //An array to store this Person's possesion strings
}
#property (nonatomic, retain) NSString *name;
#property (nonatomic, retain) NSMutableArray *possesions;
#end
In the init method's of these objects you will have to alloc/init the arrays so they will be ready to receive objects. And because I alloc'd them, my class is responsible for the release.
#implementation PeopleList
-(id) init {
if (self = [super init]) {
persons = [[NSMutableArray alloc] init];
}
}
-(void) dealloc {
[persons release];
[super dealloc];
}
#end
#implementation PeopleList
-(id) init {
if (self = [super init]) {
possesions = [[NSMutableArray alloc] init];
}
}
-(void) dealloc {
[possesions release];
[super dealloc];
}
#end
Now that this is done, you can set up your data structure of cascading array's.
As you are parsing the XML when you come across a PeopleList Tag do a:
currentPeopleList = [[PeopleList alloc] init];
and when you come across a Person tage do a:
currentPerson = [[Person alloc] init];
[peopleList.persons addObject: person];
a possesion:
[currentPerson.possesion addObject: contentsOfCurrentElement];
or name:
currentPerson.name = contentsOfCurrentElement;
But to answer your more specific question, I not store the data in a temporary NSArray and then copy that into a NSMutableArray. There is almost no performance gain by doing so, and you will burn cpu cycles and memory doing the copy.