How to encode/decode a CFUUIDRef in Objective-C - objective-c

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

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

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.

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);
}
}
}

Related

How to encode and decode a const char *

This is the first time for me to try to serialize/deserialize an object. One member of the object is a type of const char *.
----------------------------------------Added info here----------------------------------
This const char * is a byte array that represents an image. Because of image recognition requirements, I really need to get the values to the UI and back from the UI and store it as a byte array. I hope this clears up some confusion.
-------------------------------------------Ended here-------------------------------------
I am completely baffled by the options I have for both decoding and encoding. I cannot make sense out of which option to use and the only one that makes sense is asking for an at:(const void *)array. What goes there?
- (id)initWithCoder:(NSCoder *)decoder
{
self = [super init];
if (self) {
Below is the line that I cannot figure out. I have the first two parameters alright but what is the third one? The parameter for at: is an array. But I don't have an array. Am I supposed to be decoding these to an array? If so, then I need to initialize an array to put the chars into?
_templateData = [decoder decodeArrayOfObjCType:_templateData count:_templateSize at:<#(void *)#>]; //const char *templateData;
After that it seems OK:
_templateSize = [decoder decodeIntegerForKey:#"templateSize"]; //NSUInteger templateSize;
_templateQuality = [decoder decodeIntForKey:#"templateQuality"]; //int templateQuality;
_templateLocation = [decoder decodeIntForKey:#"templateLocation"]; //int templateLocation;
}
return self;
}
And then there is encoding it.
- (void)encodeWithCoder:(NSCoder *)coder
{
Same problem here. I don't have an array. Do I need to initialize an array to put the chars into? Is that how this line is supposed to work?
[coder encodeArrayOfObjCType:_templateData count:_templateSize at:<#(const void *)#>]
The rest appears OK, again:
[coder encodeInteger:_templateSize forKey:#"templateSize"];
[coder encodeInt:_templateQuality forKey:#"templateQuality"];
[coder encodeInt:_templateLocation forKey:#"templateLocation"];
}
Has anyone out there successfully encoded and decoded a type that is a const char *?
Thank you for your help.
It sounds like the best solution is to turn your array of data into an NSData instance, and encode that. It's pretty straightforward:
NSData *data = [NSData dataWithBytesNoCopy:_templateData length:lengthOfTemplateData freeWhenDone:NO];
[coder encodeObject:data forKey:#"templateData"];
Then, for decoding:
NSData *data = [decoder decodeObjectForKey:#"templateData"];
[data getBytes:_templateData length:sizeOfTemplateDataBuffer]; // Assumes _templateData has been allocated to hold sizeOfTemplateDataBuffer bytes.

error with NSSring

I'm very new to objective-c and I'm getting a basic error and unsure on how to fix it.
for(ZBarSymbol *sym in syms) {
resultText.text = sym.data; //sym.data = 0012044012482
[self phpPost:(int)sym.data];
break;
}
}
- (void)phpPost: (int)barcode {
NSString *theValue = [NSString stringWithFormat:#"%#", barcode]; //problem line
labelScan.text = theValue;
//labelScan.text = #"Barcode scanned";
}
when i use #"%#" the text of the label is correct (0012044012482), but when i use #"%d" it isn't (random number every time i restart the app). I would like to use #"%#" (or something that works) but for some reason xCode is giving me an error. and I'm unsure on how to fix the error.
The error is:
Format specifies type 'id' but the argument has type 'int'
In the end I plan on having that code (sym.data) written to a MySQL database using the POST method.
You can't just convert it to an int by casting if it's an object (which it must be if the %# format specifier isn't causing a crash).
Assuming from the fact that you're assinging it directly to a label's text that it's an NSString, you should either change the parameter type of phpPost:
- (void)phpPost: (NSString *)barcode {
labelScan.text = barcode;
}
or extract the intValue before passing sym.data:
[self phpPost:[sym.data intValue]];
and then use the proper %d format specifier in phpPost:.
Your barcode isn't an int, it is an NSString. Instead of doing (int)sym.data, pass in [sym.data intValue]. That should correctly convert it to an integer.
The reason you get a random number is because you can't just cast a string object to a primitive data type :)
I don't know what type sym.data is, but it is likely a pointer to an object, and not the value itself. You cast that pointer to int, so when you are using %d you are effectively printing the memory location of the object. That is why it changes each time you run the program (Objective-C let's you do this without any warnings - something to watch out for).
To fix this, either extract the integer value you need from the sym.data object using it's properties; or pass the object as a pointer. For instance, you could try calling your method like this:
[self phpPost:sym.data];
And then change your method to be:
- (void)phpPost: (id)barcode {
NSString *theValue = [NSString stringWithFormat:#"%#", barcode];
labelScan.text = theValue;
}
Ok, I did some thinking while I was at work today, and I figured out that an INT isn't going to work for me. if I make that object to an int, I would loss some data that is vital to what I'm doing. eg. object=001234 int=1234. I need the zeros. So, in the end, I'm keeping it an object (string) and just passing it into the function.
Here is my code after I got it working correctly.
for(ZBarSymbol *sym in syms) {
resultText.text = sym.data;
[self phpPost:sym.data];
break;
}
}
- (void)phpPost: (NSString *)barcode {
labelScan.text = barcode;
//labelScan.text = #"Barcode scanned"; //My custon label
}
Thanks, everyone for your responses. Your answer will not go unused. I'm sure I'll be needing this information here soon.
O, if you see that I did this wrong, or not the correct way, please make a comment and tell me .

Equal methods does not compute same value

I'm trying to learn objective-c. I'm trying to parse a binary file doing the following (simplified):
#interface DatFile : NSObject {
NSData* _data;
}
-(id)initWithFilePath:(NSString *)filePath;
-(void) readFile;
-(void) auxiliaryMethod;
#implementation DatFile
- (id) initWithFilePath:(NSString *)filePath {
if ( self = [super init] ) {
_data = [NSData dataWithContentsOfFile:filePath];
}
return self;
}
-(void) readFile {
int header;
[_data getBytes: &header range: NSMakeRange(0, 4)];
NSLog(#"header: %u", header);
short key;
[_data getBytes: &key range: NSMakeRange(4, 2)];
NSLog(#"key: %u", key);
short value;
[_data getBytes: &value range: NSMakeRange(6, 1)];
NSLog(#"value: %u", value);
[self auxiliaryMethod];
}
-(void) auxiliaryMethod {
short value;
[_data getBytes: &value range: NSMakeRange(6, 1)];
NSLog(#"value: %u", value);
}
My problem is that the code inside the auxiliaryMethod does not compute the same value computed by readFile method. Since the _data object is the same, why the method are computing different values? And, as you can see, the logic inside the auxiliaryMethod is just a copy of the other one...
In other languages (java) I usually separate some logic in smaller methods in order to make the code mode readable/maintainable. This is why I'm trying to mimic it with ObjC.
Of course that probably missing something, but after some hours, I gave up. I don't see where is my mistake. Probably I should erase my project and start it again...
TIA,
Bob
%u is for printing a 32 bit unsigned value. A short is 16 bits. Therefore, you are printing the parsed value plus some stack garbage.
Try %hu; the h modifies %u to print a short value.
If that doesn't work:
make sure your data objects are the same between the two methods
(don't see how they can't be... but...)
show the actual values printed
decode into an unsigned long that is explicitly initialized to 0 before decoding
With this kind of bug, it is pretty much guaranteed it is either that you aren't dealing with the input you think you are or you are dealing with some C-ism related to data types and the implied conversions in this kind of code.
I.e. assuming _data is consistent, then it indicates that the code you think is working is not actually working, but only appearing to by coincidence.
The problem is that you're passing a range of size 1 to getBytes:range:, but a short is 2 bytes in size. So getBytes:range: is only setting one of value's bytes, and the other contains random garbage.
If you really only want to get one byte from the data, change the type of value to int8_t. If you want to get two bytes, pass 2 as the second argument of NSMakeRange (and I recommend also changing the type of value to int16_t).

Passing and calling dynamic blocks in Objective C

As part of a unit test framework, I'm writing a function genArray that will generate NSArrays populated by a passed in generator block. So [ObjCheck genArray: genInt] would generate an NSArray of random integers, [ObjCheck genArray: genChar] would generate an NSArray of random characters, etc. In particular, I'm getting compiler errors in my implementation of genArray and genString, a wrapper around [ObjCheck genArray: genChar].
I believe Objective C can manipulate blocks this dynamically, but I don't have the syntax right.
ObjCheck.m
+ (id) genArray: (id) gen {
NSArray* arr = [NSMutableArray array];
int len = [self genInt] % 100;
int i;
for (i = 0; i < len; i++) {
id value = gen();
arr = [arr arrayByAddingObject: value];
}
return arr;
}
+ (id) genString {
NSString* s = #"";
char (^g)() = ^() {
return [ObjCheck genChar];
};
NSArray* arr = [self genArray: g];
s = [arr componentsJoinedByString: #""];
return s;
}
When I try to compile, gcc complains that it can't do gen(), because gen is not a function. This makes sense, since gen is indeed not a function but an id which must be cast to a function.
But when I rewrite the signatures to use id^() instead of id, I also get compiler errors. Can Objective C handle arbitrarily typed blocks (genArray needs this), or is that too dynamic?
Given that blocks are objects, you can cast between block types and id whenever you want, though if you cast the block to the wrong block type and call it, you're going to get unexpected results (since there's no way to dynamically check at runtime what the "real" type of the block is*).
BTW, id^() isn't a type. You're thinking of id(^)(). This may be a source of compiler error for you. You should be able to update +genArray: to use
id value = ((id(^)())(gen))();
Naturally, that's pretty ugly.
*There actually is a way, llvm inserts an obj-c type-encoded string representing the type of the block into the block's internal structure, but this is an implementation detail and would rely on you casting the block to its internal implementation structure in order to extract.
Blocks are a C-level feature, not an ObjC one - you work with them analogously to function pointers. There's an article with a very concise overview of the syntax. (And most everything else.)
In your example, I'd make the gen parameter an id (^gen)(). (Or possibly make it return a void*, using id would imply to me that gen generates ObjC objects and not completely arbitrary types.)
No matter how you declare your variables and parameters, your code won't work. There's a problem that runs through all your compiler errors and it would be a problem even if you weren't doing convoluted things with blocks.
You are trying to add chars to an NSArray. You can't do that. You will have to wrap them them as some kind of Objective C object. Since your only requirement for this example to work is that the objects can be inputs to componentsJoinedByString, you can return single-character NSStrings from g. Then some variety of signature like id^() will work for genArray. I'm not sure how you parenthesize it. Something like this:
+ (id) genArray: (id^()) gen;
+ (id) genString {
...
NSString * (^g)() = ^() {
return [NSString stringWithFormat:#"%c", [ObjCheck genChar]];
};
...
}
NSString * is an id. char is not. You can pass NSString * ^() to id ^(), but you get a compiler error when you try to pass a char ^() to an id ^(). If you gave up some generality of genArray and declared it to accept char ^(), it would compile your call to genArray, but would have an error within genArray when you tried to call arrayByAddingObject and the argument isn't typed as an id.
Somebody who understands the intricacies of block syntax feel free to edit my post if I got some subtle syntax errors.
Btw, use an NSMutableArray as your local variable in genArray. Calling arrayByAddingObject over and over again will have O(n^2) time performance I imagine. You can still declare the return type as NSArray, which is a superclass of NSMutableArray, and the callers of genArray won't know the difference.

Using sizeof Correctly with Byte[]

I'm sort of out of my depths here, but I have the following code (the real code actually has a point of course):
- (NSData*) dataTheseBytes:(Byte[]) bytes {
return [NSData dataWithBytes:bytes length:sizeof(bytes)];
}
The compiler warning is
Sizeof on array function parameter will return size of 'Byte *' (aka
'unsigned char *') instead of 'Byte []'
How can I eliminate this warning (or rather, what am I not understanding about my array of bytes)?
Additionally, why doesn't the error happen with this code? Must have something to do with the method signature...?
Byte bytes[3] = { byte1, byte2, byte3 };
NSData *retVal = [NSData dataWithBytes:bytes length:sizeof(bytes)];
When you pass a C array as a method or C function argument, it "decays" to a pointer to the underlying type (i.e. Byte[] is actually passed as Byte *.) So the called method/function has no idea how many elements are present in the array.
You must also pass the length of the array in order for the called code to know what you want. That's why +[NSData dataWithBytes:length:] has that second argument.
c arrays do not embed their element count.
this is how you would declare a method with an unspecified element count. this is not generally usable:
`- (NSData*) dataTheseBytes:(const Byte*)bytes;`
// or
`- (NSData*) dataTheseBytes:(const Byte[])bytes;`
a more rigid implementation could specify the element count. this is ok if you are always using the same size. example:
enum { MONByteBufferElementCount = 23 };
...
`- (NSData*) dataTheseBytes:(const Byte[MONByteBufferElementCount])bytes
{
return [NSData dataWithBytes:&bytes[0] length:MONByteBufferElementCount * sizeof(bytes[0])];
}
the problem with using objc messaging in this case is that the compiler may not be able to determine the appropriate selector and produce an error or warning if you have declared a selector with the same name but uses different parameters or element counts. therefore, it's safer to use a c function:
`NSData* DataTheseBytes(const Byte bytes[MONByteBufferElementCount]) {
return [NSData dataWithBytes:&bytes[0] length:MONByteBufferElementCount * sizeof(bytes[0])];
}
or use a more verbose name:
`- (NSData*) dataWithMONByteBuffer:(const Byte[MONByteBufferElementCount])bytes
{
return [NSData dataWithBytes:&bytes[0] length:MONByteBufferElementCount * sizeof(bytes[0])];
}
in objc, it's most common to pass the length as an argument, similar to the NSData constructor you call. some part of your program will be able to determine this value (whether it is NSData, a c array or something else).
- (NSData*) dataTheseBytes:(const Byte*)bytes length:(NSUInteger)length
{
return [NSData dataWithBytes:bytes length:length];
}
it's also common to see the element count, like so:
- (NSData*) dataTheseFloats:(const float*)floats length:(NSUInteger)count
{
return [NSData dataWithBytes:floats length:count * sizeof(float)];
}
finally, there are of course a few corner cases. the obvious being a null terminated string:
- (NSData*) dataWithASCIIString:(const char*)chars
{
return [NSData dataWithBytes:chars length:strlen(chars)];
}
You cannot pass arrays to a function. You're passing a pointer to the first element in the array of the caller.
If you need the length of that array, you need to pass that length as a separate argument to your function, and use that instead of sizeof