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
Related
I've an objective-C function like below:
- (NSValue *)foo:(NSString *)str {
NSValue* val = [NSValue valueWithPointer: str.UTF8String];
char* ptr = [val pointerValue];
return val;
}
It takes in a parameter NSString str. Inside the function, I get UTF8 representation of the the string which is a copy of characters of the string (i.e. the pointer for str and str.UT8String are different).
Now, I store this pointer information in NSValue and return this NSValue for further usage.
However, I've observed that str.UTF8String acts as a local variable to this function is a released as we come out of the function, thus leaving its pointer dangling.
Thus, when I try to use the pointer in NSValue to recreate the string, it either returns a nil or random value to me. Occasionally, it also returns true value to me.
How can I safetly save the pointer of str.UTF8String for further usage?
I am trying to pass a hex value as an unsigned int to a method using a dynamic link. The value I pass as a parameter is getting corrupted somehow. What is happening?
- (void)callPerformSelector
{
NSNumber *argument = [NSNumber numberWithUnsignedInt:(unsigned int)0xFFFFFFFF];
SEL selector = NSSelectorFromString(#"testPerformSelector:");
NSLog(#"testPerformSelector object %#", argument);
[self performSelector:selector withObject:argument];
}
- (void)testPerformSelector:(unsigned int) arg1
{
NSLog(#"testPerformSelector unsigned int %u", arg1);
NSLog(#"testPerformSelector hex %X", arg1);
}
Output is:
testPerformSelector object 4294967295
testPerformSelector unsigned int 4294967283
testPerformSelector hex FFFFFFF3
Because it should be:
- (void)callPerformSelector
{
NSNumber *argument = #0xFFFFFFFF;
SEL selector = #selector(testPerformSelector:);
NSLog(#"testPerformSelector object %#", argument);
[self performSelector:selector withObject:argument];
}
- (void)testPerformSelector:(NSNumber *) arg1
{
NSLog(#"testPerformSelector unsigned int %u", arg1.unsignedIntValue);
}
unsigned int and NSNumber * is two different things
There's an easy reason and a complex reason.
Simple reason: Why this doesn't work. The first argument to the target of a performSelectorWithObject must be an object. You are specifying a pointer to an unsigned integer in your function signature but then passing an object (NSNumber) when you call it. So instead of:
- (void)testPerformSelector:(unsigned int) arg1
you should have
- (void)testPerformSelector:(NSNumber *) arg1
You will then need to use NSNumber's unsignedIntValue to get the 0xFFFFFFFF out of the object.
The complex reason is much more interesting: Why this nearly works and looks like it loses a few bits. NSNumber is an object that wraps a numeric value this is very different from a raw numeric value. However NSNumber is implemented as a tagged pointer so although objective-c knows it is an object and treats it like an object, a subset of NSNumber values are implemented as tagged pointers where real values are coded into the "pointer" and the fact that this is not a normal pointer is indicated in the (otherwise always zero) bottom four bits of a pointer see Friday Q&A.
You can only pass objects to selectors and not primitive types, and therefore the selector should be:
- (void)testPerformSelector:(NSNumber *) arg1
{
NSLog(#"testPerformSelector hex %x", [arg1 unsignedIntValue]);
}
Update: As pointed-out by #gnasher729, the reason the number passed appears to be -13 is because it's a tagged pointer.
I have an enum and I want to pass one of its value to an Objective-C method expecting char * directly instead of creating another variable. What should I do?
e.g.
typedef enum { value1 = 0xAA, value2 = 0xBB, value3 = 0xCC } myValue;
NSMutableData *data = [[NSMutableData alloc] init];
// want to pass in the enum directly as char * but won't work
[data appendBytes:(char *){ value1 } length: 1];
// this will work, why? and is this the best way?
[data appendBytes:(char []){ value1 } length: 1];
Your example of using (char[]){value1} to get a char* that points to value1 interpreted as a char is perfectly legitimate. Equivalent code might look like
char achar = value1;
[data appendBytes:&achar length:1];
But your (char[]){value1} is obviously one fewer lines of code, so I say go with that.
For any who are confused, (char[]){value1} is a compound literal. It's a way of constructing any sort of value, including aggregates, as an expression. In this particular case, it's constructing an array of chars, with the initializer containing one element, so this constructs a char[1], which then gets implicitly converted to a pointer when passed to the method. Interestingly, you can even take the address of compound literals (although the address is only going to be valid for the full expression in which the compound literal is found), so an equivalent in this case would be &(char){value1}.
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.
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);
}
}
}