This question already has an answer here:
NSMutableDictionary setObject:forKey: fails to add key
(1 answer)
Closed 8 years ago.
I'm trying to do some complex stuff, but when I add to an nsmutabledictionary and try to get values from it, it's simply returning nothing and not printing anything when I try to print what it returns. I can't even get the following to print anything in an ibaction, even though the action is being triggered. Could you like me know if I'm doing anything wrong and what it is? Thanks!
Sample code of super simplified version of what I'm doing:
NSMutableDictionary *test;
[test setObject:#"ValueIWantToGet" forKey:#"KeyIAmSetting"];
NSLog([test objectForKey:#"KeyIAmSetting"]); //Should print "ValueIWantToGet", right?
Your test variable is not initialized. Do this instead:
NSMutableDictionary *test = [NSMutableDictionary dictionary];
or
NSMutableDictionary *test = [[NSMutableDictionary alloc] init];
In Objective-C all objects are manipulated through pointers. Hence the asterisks in declarations in front of variable names. When you do not assign anything to a pointer, its value remains uninitialized, so any reference to it is undefined behavior. In situations when the value happens to be nil, however, you would not see a crash, because Objective-C allows sending messages to nil (they have no effect).
I'm making a property of an NSMutableDictionary, so how should I initialized it if it's a property?
Properties should be initialized in the initializer of your class, for example
-(instancetype)init {
if (self = [super init]) {
_test = [NSMutableDictionary dictionary];
}
return self;
}
Related
I am write a Objective-C Code on XCode 4.4.
I have a NSMutableArray as a instance variable of my class k_info
I have defined and synthesized (nonatomic,retain) property by the name of onesplaces too.
I am unable to add a NSMUtableString object in the NSMutableArray onesplaces.
When I try to add it.The size of onesplaces remains 0 and object at zero index obviously remains null.
I tried doing this with and without using "self" key-word but it didnt worked in either case.
My syntax of adding object and printing it is right because when I create a new NSMutableArray test
and try to do the same thing it works for it but not for the instance variable onesplaces.
I cannot proceed any further in my project without solving this issue.please tell me why is it
happening and how should I solve this problem.
-(void)createinfo:(NSMutableArray )al varsis:(int)vars
{
NSMutableString stes=[[NSMutableString alloc]init];
stes=(NSMutableString*)#"string0";
[ onesplaces addObject:stes];
NSLog(#"%u",[onesplaces count]);
NSLog(#"value is: %# ",[ onesplaces objectAtIndex:0]);
[ self.onesplaces addObject:stes];
NSLog(#"%u",[onesplaces count]);
NSLog(#"value is: %# ",[self.onesplaces objectAtIndex:0]);
NSMutableArray* test=[[NSMutableArray alloc]init];
[ test addObject:stes];
NSLog(#"%u",[test count]);
NSLog(#"value is: %# ",[test objectAtIndex:0]);
}
You probably forgot to create the array. Somewhere in your code, maybe in your init method, you need to create the array before using it.
self.onesplaces = [[NSMutableArray alloc] init];
You get nil instead of error messages because Objective-C allows you to send messages to nil, which always return nil.
My goal is to add a string to array, and I do that in a method which I call.
In this method, I get a null value in the array, and don't know why. I have this at the start of my class:
NSMutableArray *listOfEvents;
and a method which I call on each event:
-(void)EventList
{
[listOfEvents addObject:#"ran"];
NSLog(#"%#", listOfEvents);
}
I get (null) in the log.
If I put the array definition NSMutableArray *listOfEvents; in the function body, I get the string value #"ran", each time, so the array always has only one value, instead of having many strings named #"ran".
What's wrong with this? It seems that I can't understand something about arrays, even though I have read the documents a number of times.
I'm assuming you haven't initialized listOfEvents.
Make sure you do listOfEvents = [[NSMutableArray alloc] init]; in your class's init method. Also make sure you release it in your class's dealloc method.
If you're getting nil in your log message, you need to make sure listOfEvents is non-nil before adding your object. IE:
-(void)EventList
{
if (listOfEvents == nil) {
listOfEvents = [[NSMutableArray alloc] init];
}
[listOfEvents addObject:#"ran"];
NSLog(#"%#",listOfEvents);
}
In Objective-C, messages with void return types sent to nil go to absolutely-silent nowhere-land.
Also, for the sake of balance, be sure you have a [listOfEvents release] call in your dealloc implementation.
Apparently you're not initializing your array.
NSMutableArray *listOfEvents = [[NSMutableArray alloc] init];
If that's your problem, I suggest reading the docs again. And not the NSMutableArray docs. Go back to The Objective-C Programming Language and others.
You need to alloc the NSMutableArray. Try doing this first -
NSMutableArray *listOfEvents = [[NSMutableArray alloc] init];
After this you could do what you what you planned...
Let's say I've got an array with strings.
NSArray *names = [NSArray arrayWithObjects: #"One", #"Two", #"Three", nil];
What I want is to initiate objects of some custom class and them add them to a mutable array. I'm using a custom init method that takes a string argument.
To be more specific, I want to [SomeClass alloc] initWithName: aName] and add the resulting object to a NSMutableArray.
I'm thinking of using Objective-C fast enumeration. So what I get is:
NSMutableArray *objects = [NSMutableArray arrayWithCapacity: [names count];
for (NSString *name in names) {
[objects addObject: [[[SomeClass alloc] initWithName: name] autorelease]];
}
The problem is that I can't add nil to the array and I don't like exception handling. However, my initiation method may return nil. So I decide to check first before adding (prevention). My new for-in-loop is:
SomeClass *someObject;
for (NSString *name in names) {
someObject = [[[SomeClass alloc] initWithName: name] autorelease];
if (someObject) {
[objects addObject: someObject];
}
}
Now, instead of immediately passing the new object to the array, I'm setting up a pointer someObject first and then passing the pointer to the array instead.
This example raises a question to me. When I someObject = [[[SomeClass alloc] initWithName: name] autorelease] in the loop, do the existing objects (which are added using the same pointer) in the array change too?
To put it in other words: does the addObject: (id)someObject method make a new internal copy of the pointer I pass or do I have to create a copy of the pointer — I don't know how — and pass the copy myself?
Thanks a lot! :-)
It's fine to reuse someObject; if you think about it, you're already reusing name each time you go through the loop.
-addObject: may or may not copy the object that you pass in. (It doesn't -- it retains the object rather than copying it, but it's conceivable that some NSMutableArray subclass could copy instead.) The important thing is that this code really shouldn't care about what -addObject: does.
Also, don't lose sight of the distinction between a pointer and the object that it points to. Pointers are just references, and a pointer is copied each time you pass it into a method or function. (Like C, Objective-C passes parameters by value, so passing a pointer into a method results in putting the value of the pointer on the stack.) The object itself isn't copied, however.
Short answer: no, you don't have to worry about reusing someObject.
Slightly longer answer: the assignment—someObject = ... assigns a new pointer value to the someObject variable; addObject: is then getting that value, not the address of someObject itself.
I think you're getting confused in the concept of pointer here. When you say someObject = [[[SomeClass alloc] init... you are basically pointing the someObject pointer to a new object. So to answer your question- your current code is fine.
As for whether arrays maintain copies of the objects added to them - NO, the array retains the object you add to it. However, that doesn't matter to your code above.
Three20 provides the answer!
I've been reading about NSArrays and NSDictionaires and I think I need the later. I'm trying to populate an object from a small database table. So I can access the string values via a record id. I have to do this several times so putting it into an object makes sense.
I have the basics...
- (void)viewDidLoad {
// WORKING START
NSMutableDictionary *dictCategories = [[NSMutableDictionary alloc] init];
[dictCategories setValue:#"Utility" forKey:#"3"];
[dictCategories setValue:#"Cash" forKey:#"5"];
NSString *result;
result = [dictCategories objectForKey:#"3"];
NSLog(#"Result=%#", result);
// WORKING END
// Can't get this bit right, current error Request for member
// 'getCategories' in something not a structure or union
NSMutableDictionary *dictCategories2 = self.getCategories;
NSLog(#"Result2=%#", [dictCategories2 objectForKey:#"5"]);
[super viewDidLoad];
}
-(NSMutableDictionary*)getCategories {
NSMutableDictionary *dictCategories = [[NSMutableDictionary alloc] init];
[dictCategories setValue:#"Utility" forKey:#"3"];
[dictCategories setValue:#"Cash" forKey:#"5"];
return dictCategories;
}
you are calling the method wrong,try [self getCategories]
You're not being clear on what isn't working, but a few things that are obviously wrong (JonLOo might be spot on though) ...
Firstly. You're using the wrong methods, or at least there's a better one -- setValue:forKey: should/could be setObject:forKey: instead. This might be one of the reasons for your issue.
Secondly. You're over-allocating and not releasing properly. dictCategories2 in your viewDidLoad will vanish into the void and bring with it the allocated memory for dictCategories defined in the getCategories method. An easy standard fix for this is to change
NSMutableDictionary *dictCategories = [[NSMutableDictionary alloc] init];
in getCategories into
NSMutableDictionary *dictCategories = [NSMutableDictionary dictionary];
It will be autoreleased using the latter method by the system.
Thirdly. You want to read up on #property. Instead of getFoo, setBar, the Ob-C standard is to use #properties to (pre)define setters and getter methods. You can then override these to populate default data into your methods when appropriate. You also (probably) want to store the dictionary in your interface as an instance variable, rather than letting it be deallocated all the time. Example of a #property implementation that does this:
#interface foo {
NSMutableDictionary *ingredients;
}
#property (nonatomic, retain) NSMutableDictionary *ingredients;
#end
// ....
#implementation foo
#synthesize ingredients;
// ...
// the #synthesize command above will create getter and setter methods for us but
// we can override them, which we need to do here
- (NSMutableDictionary *)ingredients
{
if (ingredients != nil) {
// we've already got an ingredients variable so we just return it
return ingredients;
}
// we need to create ingredients
ingredients = [[NSMutableDictionary alloc] init];
[ingredients setObject:#"foo" forKey:#"bar"]
return ingredients;
}
In the viewDidLoad method (or anywhere else where you think ingredients might not have been initialized yet), you would do e.g.
NSMutableDictionary *dict = self.ingredients;
Anywhere else you can opt to use just ingredients without self, but if it's nil, your method will never be called, and you will get nil thrown at you.
This is useful in many cases, and is necessary if we want to ever read or write the ingredients variable from outside of our class. It's outside of what you're asking about, but I brought it up because you're trying to do something similar with self.getCategories.
Hope that helps.
I want to use NSMutableDictionary to cache some data i will use later. My custom object is following:
#interface MyData : NSObject {
NSRange range;
NSMutableArray *values;
}
#property (nonatomic, retain) NSMutableArray *values;
and implement:
- (id)init {
if (self = [super init]) {
values = [[NSMutableArray alloc] init];
}
return self;
}
and when i wanna cache it, i use it like this:
NSMutableDictionary *cache = [[NSMutableDictionary alloc] init];
NSString *key = #"KEY";
MyData *data = [[MyData alloc] init];
// save some data into data
[data.values addObject:"DATA1"];
[data.values addObject:"DATA2"];
//... ...
[cache setObject:data forKey:key];
My questions is the count of cache.values is zero when i retrieve this object later as follow:
[cache objectForKey:#"KEY"];
i can retrieve "data" and the object's memory address is the same as the address when i put it into cache.
what's wrong? i need some kind guys help, any info is helpful. thanks
As Carl Norum pointed out, you're passing C strings to addObject:. addObject:, as its name suggests, requires a pointer to a Cocoa object; a C string is a pointer to characters. You need to pass NSString objects there; for literal strings, this simply requires prefixing them with #: "Fred" is a constant C string, whereas #"Fred" is a constant NSString object.
Is cache an instance variable? It looks like it's not; it appears to be a local variable, which means you're creating a new dictionary object every time. That's why there's nothing you've added previously (to previous dictionaries) in the new one. It also means you're leaking those previous dictionaries, since you're not releasing them (not in the code you showed, anyway).
Make cache an instance variable and only create the dictionary when you don't already have one (i.e., when cache == nil). Creating the dictionary in your init method is one good way. And make sure you manage its lifetime appropriately, so you don't leak and/or crash.
First of all your objects your adding don't look right it should have an # before the string. Like #"DATA1"
Second when you add an object to a dictionary or an array it does not make an actual copy of it. It just creates a pointer to it so if those objects are destroyed or moved somewhere also they are also gone out of your dictionary. A better way to make a cache of your values would be to copy the objects like so:
MyData* cache = [[MyData alloc] init];
for (int i = 0; i < [data.values count]; i ++){{
[cache.values addObject:[NSString stringWithString:[data.values objectAtIndex:i]]];
}
Don't use a dictionary in this situation.