Issue saving NSDictionary values to core data - objective-c

NOTE: Please fully read the problem before devoting it or closing it.
I have a dictionary packedData which has a value '1' in it of NSNumber type & of key "example". I save this value to core data as
myentity.attribute = [packedData valueForKey:#"example"]; //attribute is of NSNumber type as well.
when I fetch the data, the value returned is "23008" or some other unrelated value. I debugged it to find that it's a value conversion issue while saving it to core data.
Does anyone know why this occurs or its solution??
UPDATE: NSString and NSDate type are saved fine (exact values as in dictionary).

If you have "use scalar property" selected for an integer type property, the generated code is something like
#property (nonatomic) int16_t attribute;
If you uncheck that, the code looks like
#property (nullable, nonatomic, copy) NSNumber *attribute;
The first case is a raw integer type; the second one is an NSNumber object that Core Data will treat as containing an integer.
Your line of code looks like this:
myentity.attribute = [packedData valueForKey:#"example"];
The valueForKey call will return an object, in your case an NSNumber. But there's no automatic conversion between NSNumber and scalar types. So if you're using the scalar version you end up assigning the pointer value of the NSNumber. That is, you get the memory address of the NSNumber instead of the value it contains.
You can fix this either by
Not using the scalar type, so that you have an NSNumber everywhere.
Keeping the scalar type but then changing your line of code to convert the object to its integer value:
newEvent.attribute = [[packedData valueForKey:#"example"] integerValue];
The compiler should have warned you about this! I would have expected a warning reading something like incompatible pointer to integer conversion assigning to 'int16_t' (aka 'short') from 'id _Nullable'.

Related

Int set to 0 but displaying nil [duplicate]

I have an NSInteger in my class like so
In #interface:
NSInteger currentRow;
#property (assign) NSInteger currentRow;
In #implementation:
#synthesize currentRow;
Doing [self setCurrentRow:0] seems to work fine, but using [self currentRow] just returns null for some reason, not sure why. When using breakpoints I can see that the value for currentRow is 0 so it has set fine, but I can't get the value back.
In Objective-C, it's important that you distinguish between objects and primitive types.
An object is always stored as a pointer, which is the object's location in memory. A pointer is just a number. With NSLog, you can use %p to see this value. You can display it in the debugger too, like this: print myObject. A pointer is displayed as a hexadecimal number, with a 0x prefix. nil is essentially location zero (0x0000). When you allocate any kind of object, you'll get a pointer which isn't zero. When you assign an object to a variable, you are simply copying the memory address, not duplicating the object. With NSLog, you can use %# to print out an object's description. In the debugger, like this: print-object myObject.
Primitive types like NSInteger aren't objects. Instead of storing a pointer, usually you just store the value. When you assign an NSInteger variable, you make a copy of the value. You can see the value in the debugger using print. Or like this: NSLog("%ld", (long)currentRow). When you assign a primitive, you copy its value. Don't use %# or print-object with primitives — they expect objects.
(I say "usually you just store the value," because you can make pointers to primitive types, too. In situations like yours however it's not necessary.)
[self currentRow] returns 0, just like you set it. (Furthermore, because Objective-C guarantees initialization of instance variables, it'll return 0 even if you don't set it.)
The problem is that you're expecting a pointer to an object. How you fix your code depends on how you're using it:
If you're using print-object currentRow, change it to print currentRow.
If you're using NSLog("%#", currentRow), change it to NSLog(%"ld", (long)currentRow).
If you're using currentRow somewhere else, where an object is required, change your instance variable and property types to NSNumber *, an object type. Set it with [self setCurrentRow:[NSNumber numberWithInt:0]].
NSInteger is not object, it's typedef'd to int or something like that. Thus, if you set currentRow to 0 and then try to get it back, null (aka 0) is totally correct value.
The getter method will be synthesized as - (NSInteger)currentRow so it should work just fine. But how do you check if it works? With NSLog(#"%#", ...)? Than you should use %d.
If you want it to be an object you should use NSNumber and a retain property.

How do I convert an int16_t Core Data object into an int variable (and back)?

This is driving me nuts.
I've got a field in my Core Data model called "cardType" that is defined as an int16_t.
I want it to hold the setting for a UISegmentedControl.selectedSegmentIndex which is defined as an int. This particular UISegmentedControl is set for 4 segments, so an int16_t is overkill.
If I try to set
segmentedControl.selectedSegmentIndex = [dataTable cardType];
I get an error "Incompatible pointer to integer conversion assigning to 'NSInteger' (aka 'int') from 'int16_t *' (aka 'short *')"
For fun, I put an asterisk in front of the method return, ie. *[dataTable cardType], but that obviously didn't work. I also did an (int *)dataTable.cardType, but that was a stupid try as well. The compiler liked it, but the runtime threw up all over it.
I also tried a bit of multistep code where I tried to move the value into a series of NSNumber and NSInteger operations, but none of those worked either.
Another thought I just had would be to convert the datatype to an NSString and use the string functions to convert it between text and integer, but that doesn't seem very elegant.
Seems like this should be something really simple and I'm just too numb to find it.
Core Data autogenerated a pointer to an int for the #property instead of a plain int.
I changed:
#property (nonatomic) int16_t *cardType;
to
#property (nonatomic) int16_t cardType;
And everything is now working the way I wanted. Namely,
To save the segmentedControl
[dataTable setCardType:[cardTypePicker selectedSegmentIndex]];
To set the segmentedControl
cardTypePicker.selectedSegmentIndex = [dataTable cardType];
I don't know if this is an Xcode bug or not.

Why does one conversion of an array element to an integer work and the other now work?

When I convert an array element to an integer using this statement
int test=(int)[myArray objectAtIndex:2];
later use of "test" passed to other commands fails.However, this statement works
int test=[[myArray objectAtIndex2]intValue];
What is the difference between these two types of conversion?
The first is a cast. You're taking the object and casting it to an int, which will give you an int that contains the address of the object (and under 64-bit it will only contain the low 32 bits of the address). This isn't at all what you want.
The second is a method call for -intValue, which is implemented by NSNumber (and NSString) to return the int that the NSNumber (or NSString) object represents. This is (presumably) what you actually want.
The first statement is not a conversion. It is a cast of the pointer stored as element 2 in your array. The statement just assigns value of that pointer (casted to an int) to the variable.
The second statement is a converstion. It calls -intValue on object that pointer stored at that index 2 points to (possibly a NSNumber instance).
- intValue works on objects of type NSString * and NSNumber *.
If your object is actually #"123", or an [NSNumber numberWithInt:123] instead of just int 123, then using - intValue will convert it to 123. If you try and cast it directly using (int) and it's one of the above two types, then you'll run into errors accessing it as an int (because it's not, it's a more complex type).

NSInteger set to 0 but returns nil

I have an NSInteger in my class like so
In #interface:
NSInteger currentRow;
#property (assign) NSInteger currentRow;
In #implementation:
#synthesize currentRow;
Doing [self setCurrentRow:0] seems to work fine, but using [self currentRow] just returns null for some reason, not sure why. When using breakpoints I can see that the value for currentRow is 0 so it has set fine, but I can't get the value back.
In Objective-C, it's important that you distinguish between objects and primitive types.
An object is always stored as a pointer, which is the object's location in memory. A pointer is just a number. With NSLog, you can use %p to see this value. You can display it in the debugger too, like this: print myObject. A pointer is displayed as a hexadecimal number, with a 0x prefix. nil is essentially location zero (0x0000). When you allocate any kind of object, you'll get a pointer which isn't zero. When you assign an object to a variable, you are simply copying the memory address, not duplicating the object. With NSLog, you can use %# to print out an object's description. In the debugger, like this: print-object myObject.
Primitive types like NSInteger aren't objects. Instead of storing a pointer, usually you just store the value. When you assign an NSInteger variable, you make a copy of the value. You can see the value in the debugger using print. Or like this: NSLog("%ld", (long)currentRow). When you assign a primitive, you copy its value. Don't use %# or print-object with primitives — they expect objects.
(I say "usually you just store the value," because you can make pointers to primitive types, too. In situations like yours however it's not necessary.)
[self currentRow] returns 0, just like you set it. (Furthermore, because Objective-C guarantees initialization of instance variables, it'll return 0 even if you don't set it.)
The problem is that you're expecting a pointer to an object. How you fix your code depends on how you're using it:
If you're using print-object currentRow, change it to print currentRow.
If you're using NSLog("%#", currentRow), change it to NSLog(%"ld", (long)currentRow).
If you're using currentRow somewhere else, where an object is required, change your instance variable and property types to NSNumber *, an object type. Set it with [self setCurrentRow:[NSNumber numberWithInt:0]].
NSInteger is not object, it's typedef'd to int or something like that. Thus, if you set currentRow to 0 and then try to get it back, null (aka 0) is totally correct value.
The getter method will be synthesized as - (NSInteger)currentRow so it should work just fine. But how do you check if it works? With NSLog(#"%#", ...)? Than you should use %d.
If you want it to be an object you should use NSNumber and a retain property.

How to get double value from dictionary?

I'm trying to get a double value from a dictionary. How can I accomplish this in objective-c?
Dave's response to your previous question holds true for this, as well. To store a double value in an NSDictionary, you will need to box it in an NSNumber.
To set a double value in the dictionary, you'd use code like the following:
[someDict setObject:[NSNumber numberWithDouble:yourDouble] forKey:#"yourDouble"];
and read it back using the following:
double isTrue = [[someDict objectForKey:#"yourDouble"] doubleValue];
Brad Larson's response is exactly right. To elaborate on this a little more, you have to explicitly "wrap up" non-object number types (e.g., int, unsigned int, double, float, BOOL, etc.) into NSNumber when working with anything that expects an object.
On the other hand, however, some mechanisms in Objective-C, like Key-Value Coding (KVC), will automatically do this wrapping for you.
For example, if you have a #property of type int called intProperty, and you call NSObject (NSKeyValueCoding)'s valueForKey: method like [ someObject valueForKey:#"intProperty" ], the return result will be an NSNumber *, NOT an int.
Frankly, I don't care for having to switch between dealing with object and non-object types (especially structs and enums!) in Objective-C. I'd rather everything be treated as an object, but maybe that's just me. :)