We are trying to write a Wrapper from NSObject (using primitive datatypes) to a NSManagedObject (using NSNumber).
So we have to automatically convert the primitive type to a NSNumber.
We know the objCType through NSInvocation ([invocation.methodSignature getArgumentTypeAtIndex:2]) and also get the value to set ([invocation getArgument:&argument atIndex:2]) this way.
So the problem is: How to use this information to create a NSNumber out of it.
Any ideas?
There are several approaches to do this. They're all outlined under Non-Standard Persistent Attributes in the Core Data documentation.
Core Data will then convert your data on the fly to / from what you need.
Related
For an NSArray, Objective-C allows us to specify the type of the values to be stored in the array. For example, I can declare an array of NSString's as follows:
NSArray<NSString*> *arrayOfStrings;
While it is not necessarily enforced by the compiler and runtime environment, such an indication can be handy both for readability and slightly simpler code syntax.
I was wondering if such a syntax existed for NSDictionary. For example, to specify the value type in the dictionary, does something like the following exist:
NSDictionary<UIImage*> *imageMappings;
where the dictionary's values are specified to be of type UIImage.
Additional questions:
Can key types be specified for NSDictionary?
If possible, how can I specify value type without key type?
Are there other common data structures in Foundation that have this generic parametrization?
For NSDictionary, you specify the type for both key and value:
NSDictionary<NSString*, UIImage*> *imageMappings
If you don't want a specific type for the key, use id<NSCopying> or even NSObject *.
Only NSArray and NSDictionary support this notation.
Not sure why Objective-C decided to use NSNumber instead of float, double, etc. How is this type represented on disk?
NSNumber is toll-free bridged with CFNumber. In recent implementations of Core Foundation, CFNumber is a tagged pointer. This lets it be treated as an object, but without all the overhead of an object. Instead, the value is encoded in the object pointer (and isn't actually a pointer).
See Tagged pointers and fast-pathed CFNumber integers in Lion.
NSNumber is a descendant of NSObject, so it can go wherever an id can go: NSarray, NSDictionary, and so on. Primitives such as int and double cannot go in these classes, because they do not inherit from NSObject, and hence cannot participate in collections etc.
If I were to guess on the internals of NSNumber. I'd say it's a union and a type selector field. However, the beauty of encapsulation lets me successfully program to NSNumber without knowing a first thing about its representation (and not missing that knowledge).
One thing to keep in mind is that Objective-C is a super-set of C, so they didn't decide to use NSNumber instead of the primitive types (float, double, etc.) but in addition to them. If you don't need the functionality of NSNumber, then just use the primitive types and save the overhead of creating/destroying the objects. Many functions in iOS (notably the array type functions) only work with objects (descendants of NSObject). Therefore, if you want to pass some type of number to one of these functions, you need an object representation of it. This is where NSNumber comes in.
To quote the documentation on NSNumber:
NSNumber is a subclass of NSValue that offers a value as any C scalar
(numeric) type. It defines a set of methods specifically for setting
and accessing the value as a signed or unsigned char, short int, int,
long int, long long int, float, or double or as a BOOL. (Note that
number objects do not necessarily preserve the type they are created
with.) It also defines a compare: method to determine the ordering of
two NSNumber objects.
Note that internally the actual value is stored either as an integer or as a floating point number (within either a tagged pointer as Jay describes or a union in an object), depending on what value you are storing. This is important to know as if you try to store a number like "32.1" it will store it as a floating point number and when you retrieve it you will most likely get something like "32.09999999999999".
As far as storing it to disk, if you need to do this then you typically store it with encodeWithCoder and retrieve it with initWithEncoder which converts it to a format intended to be saved to disk and later read back in.
I have a core data entity with a property amount, which is a NSDecimalNumber. For this property the entity's class has a method defined with an NSDecimalNumber as argument.
In Interface Builder I bound a table column to this property (using an NSArrayController) and on the column's cell I put an NSNumberFormatter. The formatter is configured in IB as 'currency'.
Now, when I try to enter a number, I get the following error:
-[NSCFNumber decimalNumberBySubtracting:]: unrecognized selector sent to instance 0x1001d5590
Apparently my setter method is receiving a regular NSNumber rather than an NSDecimalNumber. Can I configure my formatter differently, perhaps in code rather than IB, or is the only option to add an additional setter with an NSNumber as argument?
Core Data doesn't store NSDecimalNumber, only NSNumber. NSNumber doesn't have the method that gets called.
You either need to change the entities definition to use NSNumber or build your own NSValueTransformer to store the NSDecimalNumber in Core Data.
Please look here for more details about properties: http://developer.apple.com/library/ios/ipad/#documentation/Cocoa/Conceptual/CoreData/Articles/cdMOM.html%23//apple_ref/doc/uid/TP40002328-SW1
It's not clear to me exactly where your issue is. Either the formatter is giving you a straight NSNumber instead of a NSDecimalNumber or the core data is.
To make the NSNumberFormatter give you NSDecimalNumbers, use the method -setGeneratesDecimalNumbers:
To make the data store give you NSDecimalNumbers, make sure the relevant attribute in the model is set to "decimal number".
What is the benefit of using NSNumber from Foundation Framework instead of basic C types (int, float, double)?
Using NSNumber:
NSNumber *intNumber;
NSInteger myInt;
intNumber = [NSNumber numberWithInteger: 100];
myInt = [intNumber integerValue];
Using pure C:
int intNumber;
intNumber = 100;
Seems a lot easier and economic to use C.
I know NSNumber is an object (or class?) type, but why would I use them instead simple C variables? When should I use them?
The purpose of NSNumber is simply to box primitive types in objects (pointer types), so you can use them in situations that require pointer-type values to work.
One common example: you have to use NSNumber if you want to persist numeric values in Core Data entities.
You can and should use primitives for calculations (unless with decimals, in which case you use NSDecimal or NSDecimalNumber).
If you need to pass a number as an object, use NSNumber.
If you need to make arithmetic operations, you can use int and double. If you don't want to bother with 32/64 bit issues, you can use NSInteger and CGFloat.
Because with dealing with passing of parameters with certain objects, using a basic data type will not work. Also, the NSNumber class gives you options for converting values into other datatypes quickly.
I want to have a mutable array with primitives in obj-c (selectors). What's the recommended way to do this? NSArray and those can only hold objects.
You should use an NSValue to wrap the selector or any other primitive type you need. In Cocoa SEL is some kind of pointer, so you can use [NSValue valueWithPointer:whatever] to construct it and [value pointerValue] to get it out. Or, in general you can use [NSValue valueWithBytes:&whatever objCType:#encode(SEL)]; this works for any type.
If you want to store an array of SEL objects, the easiest thing would be to convert the SELs to NSStrings using the NSStringFromSelector() function, store them in an NSMutableArray, and then convert them back to SELs when you pull them out using NSSelectorFromString() function.
Other than managing a C-style array yourself (which is definitely not the best option, IMO), your only option is to use NSArray/NSMutableArray, and store the numbers using NSNumber. It's slightly more annoying to get the value out than with the actual numeric type, but it does free you from managing the array's memory yourself.
Since the primitive types are generally just numbers (be they integer or floating-point) or pointers, what's the problem with using the classes used to wrap those up for your purposes? An NSMutableArray of NSNumbers, for example?