Using Core-Data to hold various information, one of which is a 'number' attribute (Int(16)). I try to take it out of the database using:
number = (int)[info valueForKey:#"number"];
Unfortunately, this always gives some awful result, like 2895891275 when it should be returning 3.
Would appreciate any help, thanks!
valueForKey returns an object, not an int. The fact that you're having to cast it explicitly should be a warning sign. Try:
number = [[info valueForKey:#"number"] intValue];
To expand on #duskwuffs answer:
All values in Core Data are objects. When you set an attribute type to, say, Int16, Core Data will create an NSNumber object.
This code:
number = (int)[info valueForKey:#"number"]
... gives you a huge number because [info valueForKey:#"number"] returns an instance of NSNumber, an object, which you cast to an int. When you cast an object to an int you actually cast it's address in memory to an int and so end up with a large nonsensical number.
Related
Using FMDB, bridged for use in Swift, I retrieved long integer values for a SQLite column definition like this
myColumn BigInt NULL UNIQUE
with a line of Swift code for an FMResultSet (based on a straightforward select query left out here) like this
let value = resultSet.longForColumnName("myColumn")
This worked fine. Yet, when I retrieved and then updated multiple records involving this column, I ran into a Unique Key Index violation. As it turned out, for NULL values, the above line of Swift code returned a value of 0, and I couldn't see a quick way to detect NULL values properly.
When searching for a proper way to handle this, the only related question I could find is this one concerning empty strings being returned for text columns with Null values. The answer didn't apply here. So, I'm adding the results of my research here, should they be useful to somebody else.
(The underlying problem turns out to not be specific to having the Unique constraint.)
The FMDB API, when bridged from the Objective-C version, seemingly has no direct way to check for NULL values (correct me, if I'm wrong, please). So, for a database column defined as, for example,
myColumn BigInt NULL
a NULL value will appear as value 0 in any FMResultSet involving this column with Swift code as shown in the question.
(This will have particularly surprising results when there happens to be a UNIQUE constraint on top. The NULL value will be retrieved as 0 from the database to be potentially updated as such with the next save operation, violating the Unique constraint when multiple entities are involved, as in my case. However, the underlyling problem is independent of the constraint. So, I'll focus on the problem of NULL values in general.)
To avoid this problem, we have to retrieve the column's value as an object from a respective FMResultSet first like so:
let objectValue = resultSet.objectForColumn("myColumn")
If objectValue happens to be of type/value NSNull(), then we have a NULL value and can handle it accordingly. Otherwise, we can then use the normal longForColumnName method. (For object types such as Strings, however, the FMDB implementation naturally returns an optional, which will be nil for database values of NULL!)
To make this easier, I use an extension of the FMResultSet class (for retrieval by index, which I prefer) like so:
extension FMResultSet {
func isNullForColumnIndex(columnIdx: Int32) -> Bool {
let value = self.objectForColumnIndex(columnIdx)
if let nullValue = value as? NSNull {
return true
} else {
return (value == nil)
}
}
}
This reduces the value extraction for a number type like in the example above to a line like this, assuming "myColumn" would appear at index 0 in the result set:
let num: Int64? = (result.isNullForColumnIndex(0) ? nil : Int64(result.longForColumnIndex(0)))
Surely, I could as well have added a method such as, for example, optionalLongForColumnIndex(columnIndex: Int32) -> Int64? that would include both the NULL-check and value retrieval. That would just require one such method for every value-type, which I have avoided so far.
Marco, your own answer is excellent: it describes exactly what is happening. Yet there are type-safer alternatives to ccgus/fmdb when coding in Swift. You could try https://github.com/stephencelis/SQLite.swift (pretty popular) or https://github.com/groue/GRDB.swift (closer to fmdb in the spirit).
I had a similar problem in Objective-C with FMDB.
Also getting 0 instead of nil when using longForColumnIndex.
However using:
NSNumber* object = [resultSet objectForColumnIndex:0];
worked as expected.
Gives a perfectly valid NSNumber object if a Value is stored, NSNull otherwise.
I'm pretty new to iOS development, and I want to figure out if there's a good way to handle this issue. Basically, I'm making a technical calculator that returns some product specifications based on user input parameters. The product in question has specs for some, but not all user parameters, so I . In a constants file, I have a bunch of ATTEN_SPEC_X variables which are const double or const NSString *. Now, it's perfectly okay to be missing a spec, so my plan was to leverage NSArray's ability to hold different types and use introspection later to handle strings vs doubles before I report the returned specs.
Here's an incomplete example of one method I'm implementing. It's just a big conditional tree that should return a two-element array of the final values of spec and nominal.
- (NSArray *)attenuatorSwitching:(double *)attenuator{
double spec, nominal;
{...}
else if (*attenuator==0){
spec=ATTEN_SPEC_3; //this atten spec is a string!
nominal=ATTEN_NOM_3;
}
{...}
return {array of spec, nominal} //not actual obj-c code
So instead of making spec and nominal doubles, can I make them some other general type? The really important thing here is that I don't want to use any special handling within this method; another coder should be able to go back to the constants file, change ATTEN_NOM_3 to a double, and not have to retool this method at all.
Thanks.
The problem you'll run into is that NSArrays can't directly handle doubles. However, you can get around this if you start using NSNumber instances instead - you can return an NSArray * containing an NSString * and an NSNumber * with no problems. If you need even more general typing, the Objective-C type id can be used for any object instance (though still not with primitives; you can't make a double an id).
Later, when you get an array, you can use the NSObject method -isKindOfClass: to determine the type of object you're pulling out of the array, and deal with the string or number depending on the resultant type. If you need to convert your NSNumber back to a double, just use the NSNumber instance method -doubleValue to unbox your double. (+[NSNumber numberWithDouble:] goes the other way, giving you an NSNumber out of a double.)
If you're using a recent enough version of Xcode, you can even make these things literals, rather than having to litter calls to +numberWithDouble: all over the place:
return #[ #3, #"number of things" ]
What are the benefits/downsides between using the NSInteger (or NSNumber) and float/int?
What exactly is decimal??
Thank you!
NSInteger is just a type def of int when using it on the iPhone, but on OSX will change form 32 bit int to 64 bit int if you build your code for a 64 bit machine.
NSNumber is an object that can hold any type of number, being float, integer, double, long ect..
NSDecimal is a struct which will tell you about some detail about the floating point value of a NSNumber.
NSDecimalNumber is a subclass of NSNumber which can hold a more exact floating point value: see #dreanlax comment
In Core Data, when you specify a property attribute as Integer 16/32/64, then in your code, you will always receive back an NSNumber* instance. The 16/32/64 is just a hint for the underlying store, which is probably going to be SQLite3 for how wide to make the column. If the property in Core Data is going to be used for an enum with 3 or 4 different possible values, you don't need 64bit precision for that column in the database table - so it'll make fetching/storing more efficient, and you don't waste resources.
Selecting 'decimal' as the attribute type will give you an NSDecimalNumber which has methods for performing decimal arithmetic. So, for example, if your property represents money using a decimal is a good option. (Although you will also need to store the currency scale too in that case).
NSInteger is just a typedef of int
NSNumber is a class that contains a number (float/int/...)
NSInteger and NSDecimal and the like are Foundation Data Types in Objective-C. Be sure to check the reference for more info on them.
Compared with int, an NSInteger will be considered a 32-bit integer when building 32-bit applications, and it is a 64-bit integer for 64-bit applications.
NSNumber on the other hand is a class which can be of any numeric type. Find the reference here. NSNumbers are useful when a simple data type won't do and actual objects are needed to work with such as when inserting numbers into NSArrays.
So this is the code I have:
[dataCenter.tempPalette replaceObjectAtIndex:9 withObject:selectedColour];
Object 9 does exist, and it's currently an int (not sure if that matters). selectedColour is also an int. dataCenter.tempPalette is a NSMutableArray.
The error it gives me is this:
Passing argument 2 of 'replaceObjectsAtIndex:withObject:' makes pointer from integer without a cast.
Any ideas?
ints aren't objects. If you want to store integers in an NSMutableArray, you'll need to turn them into NSNumbers first using +[NSNumber numberWithInt:]. The error message is complaining that you're passing an int where a pointer (to an object) is required.
The second param of replaceObjectAtIndex needs to be a valid pointer to an object. From your description, it sounds like you are trying to pass in an int value ( selectedColour ), which is not a valid reference to an object.
I believe, instead of passing in the selectedColour int value, you want to pass a reference to the actual color object. This would probably be a reference to a UIColor object.
Also keep in mind, if you are trying to store many objects representing the same color. For purposes of using memory efficiently, you will probably want to store in your tempPallette array references pointing to the same color object for those entries that use the same color.
Hope this helps. Good Luck.
I am deserializing some JSON using the TouchJSON framework. The array of dictionaries that comes from the parsing is used to populate a UITableView.
There is a chance that some of the values in the JSON I parse are empty. This results, if I NSLog it to the console, in the values looking like this:
id = 1234;
title = "Hello, World";
description = "<null>";
detail = "The world says hello";
Here the description value was an empty string when retrieved from the server.
So TouchJSON recognizes that the description values is of type string, but the original intention of the server was to communicate that this was an empty string, like description = #"";
If I later on try to set the value of description, to a UILabels text property the app will crash.
So my questions are, I have both NSNumbers and NSStrings in the JSON, should I traverse the result from TouchJSON's deserialize method and test all values and how would I do so?
I can't simulated what would happen if an NSNumber value was empty, how would I test for this? Will the NSNumber value be nil in that case instead of "null"?
I was using the SBJSON library and came up against the same problem. My solution would apply to your case too: I changed the library so that it handled missing values, setting them to +[NSNull null] in the collection it returned. That makes your client code a little warty, because you have to handle the cases where you might get an NSNull instead of an NSString. But this is just a more obvious version of the wart where you have to decide whether #"" meant an empty string or an unset value.