Objective-C dictionary inserting a BOOL - objective-c

OK, I'm a little confused.
It's probably just a triviality.
I've got a function which looks something like this:
- (void)getNumbersForNews:(BOOL)news andMails:(BOOL)mails {
NSMutableDictionary *parameters = [[NSMutableDictionary alloc] init];
[parameters setValue:news forKey:#"getNews"];
[parameters setValue:mails forKey:#"getMails"];...}
It doesn't matter whether I use setValue:forKey: or setObject:ForKey:, I'm always getting a warning:
"Passing argument 1 of set... makes pointer from integer without a cast"...
How on earth do I insert a bool into a dictionary?

Values in an NSDictionary must be objects. To solve this problem, wrap the booleans in NSNumber objects:
[parameters setValue:[NSNumber numberWithBool:news] forKey:#"news"];
[parameters setValue:[NSNumber numberWithBool:mails] forKey:#"mails"];

Objective-C containers can store only Objective-C objects so you need to wrap you BOOL in some object. You can create a NSNumber object with [NSNumber numberWithBool] and store the result.
Later you can get your boolean value back using NSNumber's -boolValue.

Modern code for reference:
parameters[#"getNews"] = #(news);

A BOOL is not an object - it's a synonym for an int and has 0 or 1 as its values. As a result, it's not going to be put in an object-containing structure.
You can use NSNumber to create an object wrapper for any of the integer types; there's a constructor [NSNumber numberWithBool:] that you can invoke to get an object, and then use that. Similarly, you can use that to get the object back again: [obj boolValue].

You can insert #"YES" or #"NO" string objects and Cocoa will cast it to bool once you read them back.
Otherwise I'd suggest creating dictionary using factory method like dictionaryWithObjectsAndKeys:.

Seeing #Steve Harrison's answer I do have one comment. For some reason this doesn't work with passing object properties like for e.g.
[parameters setValue:[NSNumber numberWithBool:myObject.hasNews] forKey:#"news"];
This sets the news key to null in the parameter NSDictionary (for some reason can't really understand why)
My only solution was to use #Eimantas's way as follows:
[parameters setValue:[NSNumber numberWithBool:myObject.hasNews ? #"YES" : #"NO"] forKey:#"news"];
This worked flawlessly. Don't ask me why passing the BOOL directly doesn't work but at least I found a solution. Any ideas?

Related

NSMutableDictionary most efficient way to apply formula for each value

I have a NSMutableDictionary with NSNumbers. When I finish building the set I need to recalculate all the values using the currently stored value itself. Now I'm using fast enumeration and storing into a new NSMutableSet, but I'm not experienced in Objective C and there must be a more efficient way to do this:
for (id key in temp_target_results)
{
formula_score = MyFormula([[temp_target_results objectForKey:key] doubleValue]);
[target_results setObject:[NSNumber numberWithDouble:formula_score] forKey:key];
}
In the end I'm sorting by value (that's why I'm using NSMutableSet).
I don't know how much more anyone can help you optimize what you are doing because I don't know what's going on behind the scenes (outside of the context of the snippet of code you pasted above, or what's really going on inside your MyFormula function).
One optimization question I have would be: why are you storing everything as NSNumber objects anyways and not an array of doubles? The only advantage (that I can currently see) to doing that is if you're passing your NSNumber objects along in an NSArray, NSSet or NSDictionary that gets written out to disk or passed along in an NSNotification or.
But some of the things I would do would include getting rid of that extra, unnecessary call to objectForKey:
for (NSNumber * myNumber in temp_target_results)
{
formula_score = MyFormula([myNumber doubleValue]);
[target_results setObject:[NSNumber numberWithDouble:formula_score] forKey:[myNumber stringValue]];
}
Another consideration here:
Your target_results appears to not be a NSMutableSet because you are doing a NSMutableDictionary method of setObject:forKey:. If target_results really was a NSMutableSet, you'd only need to call:
[target_results addObject: [NSNumber numberWithDouble: formula_score]];

BOOL values not recognized on CoreData objects

I encountered a strange problem regarding CoreData and boolean values:
In my data model I have set an entity's property to BOOL. Later I set theEntity.theBooleanValue = [NSNumber numberWithBool:NO] and save the object. So far so good, but when I try to check for the saved property's value with if ([theObject valueForKey:#"theBooleanValue"] == [NSNumber numberBOOL:NO]){//do something} it never jumps into the if clause. But if I check for == [NSNumber numberWithInt:0] its working... so basically I try to save a bool but it's recognized as an int... Any ideas what's going on there?
It makes more sense to examine [[theObject valueForKey:#"theBooleanValue"] boolValue] for me. i.e.,
if(![[theObject valueForKey:#"theBooleanValue"] boolValue])
{
// Your code
}
I think == operator compares the object pointer, and not the number itself. To compare number there is a separete method [NSNumber isEqualToNumber:].
Note, that'll make your life a lot easier of you declare the properties like
#property (nonatomic) BOOL theBooleanValue;
This will allow you to use
if (!theObject.theBooleanValue) {
// Your code here
}
And you can assign directly with
theObject.theBooleanValue = YES;

NSSet with NSStrings containstObject not return YES when it should

I'm loading a dictionary (list of word, not the class) into a NSSet as NSStrings. I then repeatedly send this set the message -containsObject:someNSString. But it always returns false. I wrote some code to test it:
NSLog(#"Random from dictionary: %#", [dictionary anyObject]);
NSString *test = [NSString stringWithFormat:#"BEMIRED"];
NSLog(#"To match this word: %#", test);
if ([dictionary containsObject:test])
NSLog(#"YES!");
In the log I get the following:
Random from dictionary: BEMIRED
To match this word: BEMIRED
(I'm missing the "YES!")
When I try using CFShow(dictionary) I can see that it actually contains Strings and that everything. An example:
0 : <CFString 0xc3bd810 [0x1386400]>{contents = "BEMIRED"}
3 : <CFString 0xdf96ef0 [0x1386400]>{contents = "SUBJECTIFIED"}
Can anyone please help me here?
Thanks!
NSSet uses isEqual: to test for object equality, which NSString overrides to perform a string comparison as you would expect. The follow unit test passes:
- (void)testSetStrings
{
NSSet *set = [NSSet setWithObject:#"String 1"];
// I've used the UTF8 initializer to avoid any cleverness from reusing objects
NSString *string1 = [[[NSString alloc] initWithUTF8String:"String 1"] autorelease];
// Test the references/pointers are not the same
STAssertTrue([set anyObject] != string1, nil);
STAssertTrue([set containsObject:string1], nil);
}
We can see the two strings have different pointer values, but the set still returns YES for the containsObject: call.
So I would guess your strings are not in fact equal. I would check for hidden whitespace or other similar issues.
The -[NSSet containsObject:] seems to check for the pointer value only (the documentation is very lacking for that method), not for object equality. So you need to use -[NSSet member:] instead, which uses isEqual: to check whether an object that is considered to be equal is in your set.
if ([dictionary member:test])
NSLog(#"YES!");
Edit: Actually it seems that containsObject: does use isEqual: as well. They only seem to differ in what they return (containsObject: returns a BOOL while member: returns id). I'm letting this answer stay for documentation purposes.
Ok so I solved the problem and it had nothing to do with the containsObject method. As I commented i used Dave DeLongs DDFileReader found here: Dave DeLongs DDFileReader
So by using CFShow on the entire dictionary I noticed that every word had a new line at the end of it. So instead of the -readLine method i used the -readTrimmedLine (bot methods in above mentioned file reader). This solved the problem for me.
For future forum visitors I'd like to draw attention to the discussion DarkDust and zoul had about -containsObject and -member (both methods of NSSet) which it turns out both uses the -isEqual method.

NSDictionary and EXC_BAD_ACCESS

Tried to find the answer here and eventually found a clue on another site. Posting here in case anyone searches here and has the same problem.
NSDictionary *d = [NSDictionary dictionaryWithObjects:[NSArray arrayWithObjects:#"foo", YES, 42, nil]
forKeys:[NSArray arrayWithObjects:#"bar", #"baz", #"count", nil]];
This produces:
Program received signal: "EXC_BAD_ACCESS"
What is the cause of this?
YES and 42 are not object pointers. You're trying to create an NSArray, which can only contain objects, and you're passing in values that are not pointers to objects. You'll crash for the same reason that
[YES description];
will crash -- YES is not a valid object pointer.
For one thing, in your array, YES and 42 are not objects. Try using [NSNumber numberWithInt:42] there. You should have got a compiler warning there.
A int nor a BOOL are objects and therefore cannot be part of a NSDictionary. Instead of using int's and bools use their object equivalent of a NSNumber. You can easily store a int using:
[NSNumber numberWithInt:(int)]
You can store a BOOL with:
[NSNumber numberWithBool:(BOOL)]

Cant store int or double in dictionary

When trying to store a double or an int in a dictionary I get an error
error: Semantic Issue: Sending 'double' to parameter of incompatible type 'id'
I have the following code
[data setValue:longitude forKey:#"longitude"];
longitude is a double.
How should I store this? should I just create a pointer to an int or a double?
As the other posts have stated, you need to use an NSNumber to wrap your double value in an object. The reason for this is that all the Cocoa foundation collection classes are designed to work with objects rather than primitive values. As you suggested, with some work you could in fact pass a pointer to a double value (I think, if you managed to cast it as an id type pointer), but as soon as your method finished and the double went out of scope it would be released and your pointer would now be pointing to garbage. With an object, the collection (NSDictionary, in this case) will retain your object when it's added and release it when it's removed or the collection is dealloc'ed, ensuring your value will survive until you don't need it anymore.
I would do it as follows:
NSNumber *tempNumber = [[NSNumber alloc] initWithDouble:longitude];
[data setValue:tempNumber forKey:#"longitude"];
[tempNumber release];
Which will leave your NSNumber object with only a +1 reference count (the dictionary retaining it) and no autoreleases
The other suggested method of doing:
[data setValue:[NSNumber numberWithDouble: longitude] forKey:#"longitude"];
will also work fine but your object will end up with +1 reference count an an autorelease from the numberWithDouble method. When possible I try to avoid autoreleases, but the code is more concise. YMMV.
Try using an NSNumber:
[data setValue:[NSNumber numberWithDouble: longitude] forKey:#"longitude"];
A dictionary wants an NSObject, not a number or a pointer. However it's easy to create an object containing a number:
NSNumber *mylongitudeObject = [NSNumber numberWithDouble:myLongitude];
which you can thence store in an NSDictionary.
Under the hood: mylongitudeObject will actually be a pointer to an opaque structure containing a copy of your number; but the structure also contains information so that the Objective C runtime knows what it can do with this object, such as how to copy it into a dictionary, etc.
You must use an NSNumber object instead. Try declaring longitude as follows
NSNumber longitude = [NSNumber numberWithDouble:myLongitude]
With the newer version of the compiler, you can use the Objective-C literal syntax to create a NSNumber from a variable of type double:
double longitude = 2.0;
dict[#"longitude"] = #(longitude);