views:

1085

answers:

2

What's the best way to handle Boolean values that derive from a UISwitch setting, and are stored in an NSMutableDictionary that is saved to the user's directory as persistent settings? Specifically, what's the best way to keep boolean values distinct from numeric values in an NSMutableDictionary that gets written to and read from the file system?

========

added: Thanks to Adrian and Ben. So, the upshot is that if stored via [NSNumber numberWithBool:] (this is what I have been doing) there is no convenient way to determine if that value originated as a boolean value, correct?

The second part of this is that the NSMutableDictionary that I'm storing is originally created by creating it via a plist that has Boolean values. These values have the class NSCFBoolean when first read in. Right now my strategy is to compare the value I'm working with in the dictionary with the corresponding entry in the plist.

The reason this matters is that I'm generating interface elements on the fly to allow the user to manipulate these values, and I'm associating booleans with a UISwitch. If I lose the original class by converting to a number, I get an inappropriate user interface element.

I'm considering substituting arrays as the stored value, where the first value is the stored value, and the remaining entries are the choices: where two choices exist, it is treated as a boolean... in other cases, they serve as picker values. But this seems cumbersome, if reliable.

Any ideas welcome...

+6  A: 

BOOL values are serialized as NSNumber instances, created through NSNumber's numberWithBool: static method:

BOOL ok = YES;
NSNumber *value = [NSNumber numberWithBool:ok];

// ...later...

BOOL answer = [value boolValue];

There is no other way to serialize BOOL values. In the plist that gets generated, if saved as XML, BOOL values appear as <true/> and <false/> entities.

(Edition, Jan 17 2010)

Following my comment above, this snippet of code can help identify different subtypes of NSNumber instances when they are loaded in memory, using the "objCType" message:

NSString *path = [[NSBundle mainBundle] pathForResource:@"untitled" 
                                                 ofType:@"plist"];
NSArray *array = [NSArray arrayWithContentsOfFile:path];

for (id item in array)
{
    NSLog(@"%@", [NSString stringWithCString:[item objCType] 
                                    encoding:NSUTF8StringEncoding]);
}

Where untitled.plist has this structure:

<plist version="1.0">
<array>
    <integer>123</integer>
    <true/>
    <real>1.2</real>
</array>
</plist>

In this case, the execution of the above code gives this:

2010-01-17 17:04:35.100 Untitled[62206:207] q
2010-01-17 17:04:35.101 Untitled[62206:207] c
2010-01-17 17:04:35.102 Untitled[62206:207] d

Maybe there's a different way to get the internal type of the data stored in NSNumber, but this seems to do the trick.

Adrian Kosmaczewski
Just some extra info: `q` means a 64-bit signed integer, `c` means a `BOOL` which is `typedef`-ed to a `char` in ObjC, and `d` means a `double`.
KennyTM
Thanks! I've also seen "i" (for 32-bit integers I suppose). Is there any reference for these values? I haven't found any in the documentation bundled with Xcode.
Adrian Kosmaczewski
Silly me, I've found the answer... in the NSNumber class reference :)"As with any class cluster, if you create a subclass of NSNumber, you have to override the primitive methods of its superclass, NSValue. Furthermore, there is a restricted set of return values that your implementation of the NSValue method objCType can return, in order to take advantage of the abstract implementations of the non-primitive methods. The valid return values are “c”, “C”, “s”, “S”, “i”, “I”, “l”, “L”, “q”, “Q”, “f”, and “d”."
Adrian Kosmaczewski
@Adrian: http://developer.apple.com/mac/library/documentation/cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html. There is also something rarer e.g. `j` for the C99 `_Complex` and `!` for "gc-invisible" and they won't appear in `NSNumber`.
KennyTM
+1  A: 

You can also use the constants kCFBooleanTrue and kCFBooleanFalse, though to add these to an NS-anything you'll need to cast them as IDs:

[myDictionary setObject: (id) kCFBooleanTrue forKey: @"am_I_awesome"];
Ben Gottlieb