iOS, NSMutableDictionary - iphone

I had a problem in my project where I declared in the header file an NSMutableDictionary property like so:
#property (copy, nonatomic) NSMutableDictionary *DataDict ;
Now in the implementation file, I go ahead and initialise this dictionary because I am gonna use it, like so:
DataDict = [[NSMutableDictionary alloc]init];
Now when I did this, the minute I try to add something to this dictionary I would get this error message:
-[__NSDictionaryI setObject:forKey:]: unrecognized selector sent to instance 0x885ae60 2012-10-19 16:51:56.040 testing[2297:c07] *
Terminating app due to uncaught exception
'NSInvalidArgumentException', reason: '-[__NSDictionaryI
setObject:forKey:]: unrecognized selector sent to instance 0x885ae60'
After a while and running through my project a thousand times, I decided to uncomment my initialization line, like so
//DataDict = [[NSMutableDictionary alloc]init];
and that fixed the problem.
My question is: Why?

The problem is in the way you have defined your property. If you change it to:
#property (strong, nonatomic) NSMutableDictionary *DataDict ;
instead of copy everything should be fine.
This happens because you basically say that you want a copy of your object through your generated accessors, which returns an NSDictionary instead (an immutable copy).
You can find more on objective-c properties here.
Just as a sidenote: objective-c ivars usually start with a lowercase letter (uppercase names are used for classes), so dataDict should be preferred over DataDict.

It is because the property have "copy" attribute so NSMutableDictionary instance alloc/init-ed is "copy"'ed using "copy" method, and "copy" method create not NSMutableDictionary but NSDictionary. ("mutableCopy" will create NSMutableDictionary).
Probably, you can use "retain" instead of "copy" as attributes.
#property (retain, nonatomic) NSMutableDictionary *DataDict ;
Or, just without "copy"/"retain" but use ARC.(Automatic reference counting).

I have this exact problem. No combination of retain/copy/strong/weak, etc do the trick. What does work is to create a temporary Mutable Dictionary, load it up and then set my original equal to it.
NSMutableDictionary * tempD = [[NSMutableDictionary alloc] init];
[tempD setObject: epsTapeCut forKey:LWPrintParameterKeyTapeCut];
[tempD setObject: epsCopies forKey:LWPrintParameterKeyCopies];
[tempD setObject: epsHalfCut forKey:LWPrintParameterKeyHalfCut];
[tempD setObject: epsPrintSpeed forKey:LWPrintParameterKeyPrintSpeed];
[tempD setObject: epsDensity forKey:LWPrintParameterKeyTapeWidth];
self.ePSprintSettings = tempD;
This fails:
[self.ePSprintSettings setObject: epsTapeCut forKey:LWPrintParameterKeyTapeCut];
Declaration:
#property (nonatomic, retain) NSMutableDictionary *ePSprintSettings;
(But again, no combination of attributes makes a difference.)
Initialization:
self.ePSprintSettings = (NSMutableDictionary *)[myUserDefaults dictionaryForKey:kEpsPrintSettings];
Thank you for helping me understand.
I'm happy enough that this works, but I'd like to understand why.

Related

Saving Arrays Between Classes

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.

Which way is the correct way to allocate memory with a property?

Which way is correct?
NSString *c = [[NSString alloc] init];
self.character = c;
[c release];
or
self.character = [[NSString alloc] init];
And why? Thanks.
It depends on how your property is declared. If you used
#property (nonatomic, retain) NSString *someString;
The setter will be created to retain someString, in which case the correct way is your first method. If you used:
#property (nonatomic, assign) NSString *someString;
Your second method will be correct, since it will just assign the pointer and not retain anything.
It depends on how you've defined your property.
If it's copy or retain, the synthesized setter (setCharacter: in your example) will take ownership of any objects you assign to the property. In this situation your first example is correct. The second would lead to a memory leak as you've claimed ownership of the NSString twice and you will (probably) only relinquish ownership once; thus the memory can never be reclaimed.
If it's assign on the other hand, the setter won't do anything special and your second example would be correct. The first would result in an EXC_BAD_ACCESS error if you tried to do anything with the NSString. I should note that you generally only use assign for primitive types and delegates.
I suggest you have a read over the Memory Management Programming Guide and the Declared Properties section of The Objective-C Programming Language guide.
The answer depends on your #property definition. Likely it's something like (retain) or (copy) for an NSString. In that case, assigning to self.character will increment the retain count. So the bottom:
self.character = [[NSString alloc] init];
You've set the retain count to 1 with the alloc and self.character will also retain it for a count of 2, so that'll leak. Should be
self.character = [[[NSString alloc] init] autorelease];
or the top version.
The answer to this now with iOS 5 is to use ARC.

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.

NSString "invalid summary"

Ok, I have read a lot of posts and resources about this but I am STILL having the same issue. I have a few NSString variables that I need to be class-wide variables used in multiple places throughout the class. I have tried many combinations of settings. First of all, I do have the strings declared in the interface like so:
#interface iCodeViewController : UIViewController <NSXMLParserDelegate> {
NSString *myString;
}
I have also added the property as follows (I have tried with and without the property and synthesizing)
#property (readwrite, retain) NSString *myString;
I have also tried, (nonatomic, retain), (nonatomic, copy), (readwrite, copy).
Then in the .m file:
#synthesize myString;
I have tried:
self.myString = #"whatever";
myString = #"whatever";
I have also tried with and without allocating memory to it by:
myString = [[NSString alloc] init];
What am I missing??
After I have 'supposedly' set the string variable in one method, I try to check it in another with if ([myString isEqualToString:#"blah blah"]) and when I put in a breakpoint and hover over myString it is always showing 'invalid summary.
Thanks!
use below
#property (nonatomic, retain) NSString *myString;
self.myString = [NSString stringWithString:#"whatever"];
for more read the SO post
Invalid Summary in NSString assignment
Can you place the class code here? The way you are handling your myString is perfectly fine.
One possibility I can think of is that you are forgetting to return self from the init method.
There could be other possible memory related mess some where in your code.
I was able to reproduce the invalid summary by initializing an NSMutableString to size 100 and only appending to it. I found the problem went away when I called
[mutableString setString:#""];
prior to
[mutableString appendString:string];

Unable to setObject:forKey: in NSMutableDictionary

I'm sure I'm missing something obvious here in a simple iPhone program I'm trying to write, but the code seems so basic that I can't see what's wrong.
I'm trying to use an NSMutableDictionary to store a list of classes and their associated save file names. In the header file I declare the dictionary
#interface ClassList : UITableViewController {
NSString *homedirectory;
NSString *masterindexpath;
NSMutableDictionary *classFilenameGlossary;
NSMutableArray *listofclasses;
}
#property (retain, nonatomic) NSString *homedirectory;
#property (retain, nonatomic) NSString *masterindexpath;
#property (retain, nonatomic) NSMutableDictionary *classFilenameGlossary;
#property (retain, nonatomic) NSMutableArray *listofclasses;
And, of course, in the implementation file:
#implementation ClassList
#synthesize homedirectory;
#synthesize masterindexpath;
#synthesize classFilenameGlossary;
#synthesize listofclasses;
I initialize this dictionary at ViewDidLoad from an existing file that saves the classlist:
- (void)viewDidLoad {
[super viewDidLoad];
// Get home directory
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
homedirectory = [paths objectAtIndex:0];
masterindexpath = [homedirectory stringByAppendingPathComponent:#"MasterIndex"];
// Get master course list or create it if necessary
NSFileManager *fm = [NSFileManager defaultManager];
if ([fm fileExistsAtPath:masterindexpath] == NO)
{
NSMutableDictionary *temporarydictionary = [NSMutableDictionary dictionary];
[temporarydictionary writeToFile:masterindexpath atomically:NO];
[temporarydictionary release];
}
[classFilenameGlossary initWithContentsOfFile:masterindexpath];
[homedirectory retain];
[masterindexpath retain];
}
Later, I add an item using setObject:forKey: but the dictionary never changes. Originally I was doing this in another view controller using a pointer back to this ClassList file, but once I discovered that didn't work, I simplified and tried to just set any sample object and key within the ClassList file:
[classFilenameGlossary setObject:#"sample filename" forKey:#"sample classname"];
I've looked at the debugger variable list and it shows classFilenameGlossary properly identified as an NSMutableDictionary with 0 key/value pairs. I'm not getting any sort of error; the dictionary simply never changes. At the moment, the dictionary loaded from the file masterindexpath is empty (since I haven't been able to add any items yet), but that shouldn't keep me from being able to add items to the dictionary.
I'm a total beginner at iPhone programming, so I'm sure there's something basic that I'm missing. I'd appreciate any help.
This line:
[classFilenameGlossary initWithContentsOfFile:masterindexpath];
should look like this:
classFilenameGlossary = [[NSMutableDictionary alloc] initWithContentsOfFile:masterindexpath];
You forgot to allocate memory for the NSMutableDictionary, so that's why it never initializes.