NSValue and non-object types

When you want to use the collections of the Cocoa for storing non-object types, NSValue and NSNumber are very useful. NSNumber is a child class of the NSValue, and therefore the NSValue has more flexibility.
Let’s take look at the description on what the NSValue can do :

An NSValue object is a simple container for a single C or Objective-C data item. It can hold any of the scalar types such as int, float, and char, as well as pointers, structures, and object ids. The purpose of this class is to allow items of such data types to be added to collection objects such as instances of NSArray or NSSet, which require their elements to be objects. NSValue objects are always immutable.

So, the following code is possible.

// assume ImaginaryNumber defined:
typedef struct {
float real;
float imaginary;
} ImaginaryNumber;

ImaginaryNumber miNumber;
miNumber.real = 1.1;
miNumber.imaginary = 1.41;

NSValue *miValue = [NSValue value: &miNumber
withObjCType:@encode(ImaginaryNumber)]; // encode using the type name

ImaginaryNumber miNumber2;
[miValue getValue:&miNumber2];

Impressive, isn’t it?
However, Apple’s document has a one strange line of explanation which is somewhat ambiguous.

Note that the type you specify must be of constant length. You cannot store C strings, variable-length arrays and structures, and other data types of indeterminate length in an NSValue—you should use NSString or NSData objects for these types. You can store a pointer to variable-length item in an NSValue object.

What does that mean? What if your data is not variable length? Will it be stored correctly?

typedef struct {
int dataSize;
char *data;
int year;
} myStructType1;

When the data points to a character array, will it be encoded correctly?
This is easy to answer. It is variable length. So, the content the data points to is not encoded.
Only the address is encoded. So, if you write a publisher thread which encodes the type and releases allocated memory space for the data, and a consumer thread which decode it to retrieve original data, the decoded myStructType1 instance only contains the address for the data, not the content. So, don’t expect it would store the data. For the purpose, use NSData or NSArchiver.

How about this example?


typedef struct {
int age;
int month;
int day;
} innerType;

typedef struct {
int dataSize;
innerType *innerData;
} myStructType2;

 
Well.. the innerType is sort of non-variable-length type. So, will it be encoded correctly?
No. Apple’s document is not clear on this. It also encode the address not the content.
So, in this case, you also need to use the NSData.
So, try using the NSValue to convert struct without any member which is a pointer to some other structure.

Then how the NSValue stores? It is kind of shallow copy. Please read this.

Here the address of myCString is passed (&myCString), so the address of the first character of the string is stored in theValue. Note that the NSValue object doesn’t copy the contents of the string, but the pointer itself. If you create an NSValue object with an allocated data item, don’t deallocate its memory while the NSValue object exists.

3 responses to this post.

  1. Posted by Daniel Phillips on February 15, 2010 at 6:43 AM

    Thanks for the article, you helped me solve this obstacle which has been holding me up all day, I’m writing a class to store all XML data into an NSMutableDictionary, The way I see it, the NSXMLParser with event-driven XML reading is good for large XML data which hold list-type information, such as RSS, or just lists of some kind…
    But sometimes, you’re XML isn’t so clean and structured, for example, a settings.xml file may contain only a dozen elements and hold information your application needs.
    using NSXMLParser on these types of XML data can seem like you’re coding an entire application for that single small XML file, so I am programming a way to easily access bits of information for small to medium sized “various information” xml data.
    Send me a mail if you’re interested in knowing more…

    Reply

  2. I found this post helpful. The fact that a pointer to a value is stored really is the key to understanding why variable length data types wouldn’t be correctly stored in NSUserDefaults. Thanks for the info.

    Reply

Leave a comment