views:

341

answers:

3

I want to have a GUID in my objective-c model to act as a unique id. My problem is how to save the CFUUIDRef with my NSCoder as its not a an Object type.

I keep playing around with the following lines to encode/decode but I can't seem to find any good examples of how to save struct types in objective-c (all of my NSObject types are encoding/decoding fine).

e.g. for encoding I am trying (which I think looks good?):

CFUUIDBytes bytes = CFUUIDGetUUIDBytes(uuid);
eencoder encodeBytes: &bytes length: sizeof(bytes)];

and for decoding which is where I get more stuck:

NSUInteger blockSize;
const void* bytes = [decoder decodeBytesForKey: kFieldCreatedKey returnedLength:&blockSize];
if(blockSize > 0) {
     uuid = CFUUIDCreateFromUUIDBytes(NULL, (CFUUIDBytes)bytes);
}

I gt an error "conversion to a non-scaler type" above - I've tried several incarnations from bits of code I've seen on the web. Can anyone point me in the right direction? Tim

+1  A: 

The easier (but a bit more inefficient) way is to store it as an NSString (CFString) using CFUUIDCreateString, and recover the UUID with CFUUIDCreateFromString.

KennyTM
Or you could store it as `NSData` by grabbing the bytes using `CFUUIDGetUUIDBytes` and using the `dataWithBytes:length:` method of `NSData`.
Rob Keniger
Actually - the NSData route is something I had forgotten about, and I think it looks promising. Thanks
TimM
I should also have added that I didn't want to use the CFString option as I should be able to store the UUID object directly (and as noted, more efficiently than converting to and from a string)
TimM
@TimM: Use Eyal's solution in that case. Unless you un/archive the UUID very often the efficiency doesn't matter. In fact, un/archiving probably takes more time than converting a UUID from/to string.
KennyTM
+1  A: 

The problem with your code is the last line of the decoding, "bytes" is a pointer to a CFUUIDBytes struct and you're trying to cast it as if it is the CFUUIDBytes struct itself, which is not correct and is correctly detected by the compiler. Try changing the last line to:

uuid = CFUUIDCreateFromUUIDBytes(NULL, *((CFUUIDBytes*)bytes));

The idea here is that you cast "bytes" to be a pointer to CFUUIDBytes (inner brackets) and then dereference the casted pointer (outer brackets). The outer brackets are not strictly necessary but I use them to make the expression clearer.

Eyal Redler
This definitely got me further - and I spotted a problem with my encoding example, I wasn't setting a key - which then threw up a problem with how I was passing the bytes of the uuid. Would the following be the symetrical saving for your line:[encoder encodeBytes: (void*)
TimM
The above seems to work however its feels more C style than objective-C (although many thanks for getting me on track). I am thinking that the comment on using NSData might give a less low leverl solution.
TimM
Using NSData will not give you a more high (or low) level solution since in both cases you're using CFUUIDCreateFromUUIDBytes and CFUUIDGetUUIDBytes. The terms low/high level are irrelevant here.The difference between the solutions is the method of encoding/decoding the bytes you get or set to the uuid. Both are valid (although using "encodeBytes" would probably mean less code).The important point here is to make sure you understand why the code you initially posted failed and why the fixed code works.
Eyal Redler
Agreed - the reason I didn't go with the CFString approach was that I wanted to learn how the underlying details should work (and your answer shows that). I will post the full NSData solution as I am interested in what others think is the best style.
TimM
A: 

Based on the answers given, I tried the casting approach given by Eyal and also the NSData approach suggested by Rob, and I thought that the latter seemed clearer, although I am interested in what others think.

I ended up with the following:

- (void)encodeWithCoder:(NSCoder *)encoder {  
    // other fields encoded here
    CFUUIDBytes bytes = CFUUIDGetUUIDBytes(uuid);
    NSData* data  = [NSData dataWithBytes: &bytes length: sizeof(bytes)];
    [encoder encodeObject: data forKey: kFieldUUIDKey];
}

- (id)initWithCoder:(NSCoder *)decoder {
    if (self = [super init]) { 
        // other fields unencoded here
        NSData* data = [decoder decodeObjectForKey: kFieldUUIDKey];
        if(data) {
          CFUUIDBytes uuidBytes;
          [data getBytes: &uuidBytes];
          uuid = CFUUIDCreateFromUUIDBytes(NULL,uuidBytes);
    } else {
          uuid = CFUUIDCreate(NULL);
        }
    }
  }
TimM