Cant store int or double in dictionary - objective-c

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);

Related

Objective-C and ARC: Why value stored to during its initialization is never read and Incompatible pointer

when I run the analyzer tool I'm getting value stored to (jsonArr and myrrh) during its initialization is never read on lines and Incompatible pointer types assigning to 'NSMutableArray * from 'NsDictionarty *'
Look at the 1st two lines:
NSDictionary *jsonArr = [[NSDictionary alloc] init];
jsonArr = [json objectForKey:#"categories"];
The first line creates and assigns a new dictionary.
The second line then reassigns a new value to the same variable. This throws away the original value. Hence the warning that the value is never used.
Those two lines should simply be:
NSDictionary *jsonArr = [json objectForKey:#"categories"];
Actually, even better would be:
NSDictionary *jsonDict = json[#"categories"];
Don't name a dictionary variable with Arr. It's confusing. And use modern syntax.
The issues with myArr are the same. Though you have the additional problem of trying to assign an NSDictionary to an NSMutableArray. Those two are in no way compatible.
Perhaps jsonArr is properly named and its type should be NSArray instead of NSDictionary. Even then, you can't assign an NSArray to an NSMutableArray. You need to make a mutable copy:
NSArray *jsonArr = json[#"categories"];
NSMutableArray *myArr = [jsonArr mutableCopy];
In Obj-C variables are typed, meaning only values conforming to that variable's type may be assigned.
In your code (which is so hard to read) myArr is of type NSMutableArray * (a reference to an NSMutableArray) while jsonArr is of type NSDictionary* (a reference to an NSDictionary)
You cannot assign the value in jsonArr to myArr without a type conversion operation (cast).

Explanation for assigning Objective-C NSNumber object to int variable?

Can anyone explain why this works in Objective-C? I would expect it to give an error since an object is being assigned to an int variable. I get that it does work, and this is great, but I am missing why this is allowed?
int i = [NSNumber numberWithInt:123];
Furthermore, this seems to store the wrong value (ie, on my system when I print out the value of "i" using NSLog I get "252711" instead of 123); however, only when the variable is declared and assigned in my main code. If I declare it as an instance variable of an object and then use Key-Value coding to assign it a value, it works properly:
Object instance variables...
#interface myObject : NSObject
{
int anInt;
}
Main code...
[myObject setValue:[NSNumber numberWithInt:123] forKey:#"anInt"];
NSLog(#"%#", [myObject valueForKey:#"anInt"]);
This code prints out "123" as expected, but I'm not sure why given that when using a seemingly similar approach above it does not work properly.
it doesnt "work" and there is a compiler warning about it. the reason it can be compiled is that the NSNumber class method numberWithInt returns a pointer, which can be implicitly converted to int. When you print it out you are getting the address where the objective-c object was allocated.
the SetValue:forKey: method doesnt take an int parameter, it takes an id which is just a pointer to a generic Objective-C object. Key-Value coding is taking care of assigning the intValue of the NSNumber object for you.
When you executing the code:
int i = [NSNumber numberWithInt:123];
You just assigning pointer value to int.
numberWithInt: returns NSNumber* and it's a pointer, pointing to some place in memory where NSNumber object allocated.
And now value of i is not a 123 but decimal representation of NSNumber* pointer.
Read more about pointers in C/Objective-C.

nsnumber into uipasteboard

I'd like to copy a float into the pasteboard, but the important thing is the value, as I want to paste it later in numbers, as a number.
Tried with :
[pasteboard setValue:SomeNSNumberWhereIStoredTheFloat forPasteboardType:#"NSNumber"];
With that, it got nothing to paste, and with pasteboard.string = numberInStringValue, it pastes the number as a series of characters, in what I'm not interested.
Thanks for your help
The "type" of pasteboard data is not the name of a class, it's a Uniform Type Identifier (UTI, or just UT if you remember what else UTI stands for.) In this case, your data does not have an associated UTI (numbers are abstract concepts, not data formats.) You'll have to figure out the best way to store that number and retrieve it.
I think in this case, formatting the number into a string will suffice:
NSString *numString = [NSString stringWithFormat:#"%f", theFloatValue];
pasteboard.string = numString;
And later, when getting it back:
float theFloatValue2 = [pasteboard.string doubleValue];
This does not take into account checking for nil or other error handling.
If you need very high precision, you may need to investigate an NSData-based storage technique.
You can store an NSNumber directly. You can used the following methods from the API
setValue:forPasteboardType:
Use this method to put an object on the pasteboard that is a standard property-list object that is an object of the NSString, NSArray, NSDictionary, NSDate, NSNumber, or NSURL class.
valueForPasteboardType:
This method attempts to return an object that is of a class type appropriate to the representation type, which typically is a UTI. For example, if the representation type is kUTTypePlainText (public.plain-text), the method returns an NSString object. If the method cannot determine the class type from the representation type, it returns the object as a generic property-list object. Property-list objects include NSString, NSArray, NSDictionary, NSDate, or NSNumber objects, with NSURL objects also as a possibility. If the method cannot decode the value as a property-list object, it returns the pasteboard item as an NSData object.
The real problem comes in finding the correct UTI so the class will automatically give you back an NSNumber and not give you back an NSData object instead.
To make matters worse, the code doest not appear to work as the advertised by the documentation. I've heard from several people the method will always return you NSData. You can find an example (and a workaround) of such issue in this answer.
You can store your float value as NSNumber.
But NSNumber is not stored in UIPasteboard correctly although docs states it does (bug?).
To keep NSNumber in UIPasteboard you should archive NSNumber to NSData, and to retrieve NSNumber from UIPasteboard you should unarchive NSData back to NSNumber.
// adding data to pasteboard
NSNumber *number = [NSNumber numberWithDouble:floatValue]; // store your value here
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:number]; // archive NSNumber to NSData
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:data, #"yourKey",nil];
[[UIPasteboard generalPasteboard] addItems:[NSArray arrayWithObject:dict]];
// retrieving data
NSData *data = [dict valueForKey:#"yourKey"]; // here dict is properly obtained NSDictionary of pasteboard object
NSNumber *number = [NSKeyedUnarchiver unarchiveObjectWithData:data]; // unarchive NSData to NSNumber

Objective-C dictionary inserting a BOOL

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?

How to use #encode() to get #"NSArray" in Objective-C

I'm using the runtime functions to get the type of a property (thanks to eJames for helping me to figure out this way).
The attribute string of the property looks like this:
T#"NSArray",&,Vstuff
I need to check if the property type is an array, at the moment I'm doing it like this:
- (BOOL)valueForKeyIsArray:(NSString *)key fromTagret:(id)target
{
NSString *lowerCaseKey = [self convertToKVCKey:key];
objc_property_t property = class_getProperty([target class], [lowerCaseKey UTF8String]);
NSString *propertyAttrs = [NSString stringWithUTF8String:property_getAttributes(property)];
NSString *encodedType = #"#\"NSArray\"";
NSRange range = [propertyAttrs rangeOfString:encodedType options:NSLiteralSearch];
return range.location != NSNotFound;
}
But since Apple can change the type definition string at any time, I would like to generate this #"NSArray" type string. I tried it with #encode(), but it did not work:
NSString *encodedType = [NSString stringWithUTF8String:#encode(NSArray *)];
So how can I generate this type string? Or is there a better way to check if this property attributes contain the array type?
There is no way to check this. In Objective-C source code the variables being typed as NSArray * is only there for the compiler to issue warnings. It has no meaning, and does not exist at runtime. If you mis-typed an NSArray as an NSString, you would get lots of warnings when compiling, but your code would behave exactly the same when run. At runtime all that is known is that the ivar/property is "an object".
Another way to think of it, is that once Objective-C is compiled, all object references are id references.
Just accept that if the runtime changes, your code will break, and move on. However, I think you might be miscategorizing ivars of type NSMutableArray *, CFArrayRef, or CFMutableArrayRef. You also seem to be assuming all keys correspond directly to a declared property.
The cleanest solution might be to assert that the sample object being used for the test (the target) must have a non-nil value for that key, and just grab the value and test that [[target valueForKey:key] isKindOfClass:[NSArray class]].