Passing an object that undergoes default argument promotion to 'va_start' - ios

This is my first Xcode app and objective-c so give me some slack :)
I tried googling on the issue but I cannot see any help regarding Xcode and app development. I added the error masseages after //
- (id)initWithBytes:(int8_t)byte1, ... { //Error: 1. Parameter of type 'int8_t' (aka 'signed char') is declared here
va_list args;
va_start(args, byte1); //Error: Passing an object that undergoes default argument promotion to 'va_start' has undefined behavior
unsigned int length = 0;
for (int8_t byte = byte1; byte != -1; byte = va_arg(args, int)) {
length++;
}
va_end(args);
if ((self = [self initWithLength:length]) && (length > 0)) {
va_list args;
va_start(args, byte1); // Error: Passing an object that undergoes default argument promotion to 'va_start' has undefined behavior
int i = 0;
for (int8_t byte = byte1; byte != -1; byte = va_arg(args, int)) {
_array[i++] = byte;
}
va_end(args);
}
return self;
}
Thank you in advance!!

va_start() saves the pointer to the first argument passed to the function into a va_list.
The arguments themselves are passed via a hardware stack.
The issue with int8_t comes from the way the hardware stack is implemented. (in x86 at least)
Just like the SSE and MMX does, the stack requires elements stored on it to have an alignment equal to a multiple of 16bits, so everything passed to the function WILL have at least 16 bits of size, regardless of its type.
But the problem is va_arg() doesn't know about all that. Historically, it was a macro, and all it does is returning a pointer stored in va_list, and incrementing va_list by sizeof(type).
So, when you retrieve the next argument, the pointer returned does not point to the next argument but one byte before it, or not - depending on whether the va_arg is a macro or a compiler built-in function.
And this is what a warning is about.
IMO at least. Pardon my English, It's my 2nd language.

Related

Objective-C passing object to function is always by reference or or by value?

In objective-c I am passing NSMutableDictionary to function and modifying it inside function it returns modified mutable dictionary :
NSMutableDictionary *obj2 = [[NSMutableDictionary alloc]initWithObjectsAndKeys:#"hello",#"fname",nil];
[self callerDictionary:obj2];
NSLog(#"%#",obj2[#"fname"]);//printing "Hi"
-(void)callerDictionary:(NSMutableDictionary*)obj
{
obj[#"fname"] = #"Hi";
}
Technically, Objective C always passes parameter by value, as does C, but practically when you pass an object you need to pass a pointer. While this pointer is passed by value, the semantics of Objective-C give the same effect as if you had passed an object reference; if you modify the objected that is pointed to by the pointer then you are modifying the same object instance that is pointed to in the calling context. The common terminology used in Objective C programming is "object reference" even though it is really a pointer value.
You can see from the * in the method signature that it is a pointer (or object reference in the common usage). If you are passing an intrinsic type, such as an int then it is passed by value unless you explicitly declare the method as requiring a reference:
For example:
-(void) someFunction:(int *)intPointer {
*intPointer = 5;
}
would be called as
int someInteger = 0;
[self someFunction: &someInteger];
// someInteger is now 5
The distinction between a pointer value and a true object reference can be seen in comparison to Swift which uses true references;
If I have
-(void)someFunction:(NSString *)someString {
int length = [someString length];
}
and then do
NSMutableArray *array = [NSMutableArray new];
[someFunction: (NSString *)array];
I will get a runtime exception since array doesn't have a length method, but the compiler can't confirm the type I am passing since it is a pointer.
If I attempted the equivalent in Swift then I will get a compile time error since it knows that the type coercion will always fail
All objects in Objective C passed by reference.
All C types such as NSUInteger, double etc. passed by value
C and Objective-C always pass parameters by value. Objective-C objects are always accessed through a reference (i.e. a pointer). There is a difference between a variable type (int, pointer, etc.) and the way variables are passed as function parameters. The use of the term reference in both scenarios can cause confusion.
by-value:
void f(int a) {
a = 14;
}
int a = 5;
NSLog(#"%d", a); // prints: 5
f(a);
NSLog(#"%d", a); // prints: 5
The value 5 is printed both times because the function f() is given a copy of the value of a, which is 5. The variable referenced within the function is not the same variable that was passed in; it is a copy.
In C++, you can have functions that take parameters by reference.
by-reference:
void f(int &a) {
a = 14;
}
int a = 5;
NSLog(#"%d", a); // prints: 5
f(a);
NSLog(#"%d", a); // prints: 14
Note the & in the function signature. In C++ (but not C, nor Objective-C), this means that the parameter is passed by reference. What this means is that a reference (pointer) to a is passed to the function. Within the function, the a variable is implicitly dereferenced (remember, it's really a pointer, but you don't treat it as one), and the original a variable declared outside the function is changed.
In C and Objective-C, passing a pointer to a function is functionally equivalent to using a reference parameter in C++. This is because a copy of the address is given to the function (remember, the parameter is still passed by value), and that address points to the same object instance that the original pointer does. The reason you don't see any explicit pointer dereferencing within the function (similar to the C++ reference) is because Objective-C syntax for object access always implicitly dereferences -- being within a function doesn't change this behavior.

calling a function with a char and a variable number of arguments

so what i am trying to do is whenever i touch a button, it calls a function with both a char and some variable number of arguments (in this case two ints that are both worth 24).
- (IBAction)buttonPressed:(id)sender {
number = 24;
printf_to_debug("some text %d %d ",number, number);
//printf(" %d %d ",number, number);
}
however, when i call the function it seems to always change my ints to some big number, which i don't understand.
void printf_to_debug (char* fmt, ...) {
va_list args;
va_start( args, fmt );
printf(fmt, args);
charString = [NSString stringWithUTF8String: fmt];
charString = [NSString stringWithFormat:charString, args];
debugTextString = [NSString stringWithFormat:#"%#\r%#",charString, debugTextString];
va_end( args );
}
does anyone see a problem with my code?
You have to use vprintf() to print the arguments given by a va_list:
va_list args;
va_start (args, fmt );
vprintf(fmt, args);
va_end( args );
The corresponding NSString method is initWithFormat:arguments::
NSString *s = [[NSString alloc] initWithFormat:#(fmt) arguments:args];
Let's take a look at what your code actually does.
The first important line is: printf_to_debug("some text %d %d ",number, number);
Which looks fine, it doesn't really matter what number is it'll be treated as an int anyways.
Next you initialize the variable arguments list which is fine but then you try and print using:
printf(fmt, args);
Now, When you use that code, you seem to want printf to treat each individual object in your variable arguments list as a separate parameter, however, printf doesn't care. All it sees is that you passed it a parameter. It doesn't know or care what the parameter was, all it knows and cares about is that you told it to treat the first parameter it was given as a decimal number (that's what the "%d" says). So now, it takes va_list and treats it as a number and prints it. Since you only provided one argument, the second number you get is complete garbage, and I suspect XCode tried to warn you about that (command line clang does).
Now for the simple solution:
When your trying to print a variable arguments list as seperate objects use the vprintf function. So in your case it would be vprintf(fmt, args);

BOOL property from a calculation returns NSNumber with incorect value using valueForKey:

I have a simple object which has one NSNumber which is used to store some flags.
I have a conienience getter method which in fact does:
[self.flags integerValue] & SomeConstantFlag
for a property#property (readonly, nonatomic, assign) BOOL someConstantFlag
and this works fine when accesing the underlying bool value like
model.someConstantFlag
but when I try to
id value = [model valueForKey:#"someConstantFlag"];
Then it returns a bad boolean representation e.g. NSNumber with value 2, 4 etc.
Why is this happening when the declaration of the property is BOOL? Is there a "Pretty" way to overcome this issue?
Wrapping on the other hand works ok:
BOOL someBool = 42;
NSNumber* numberVal = #(someBool);
//The underlying is an __NSCFBoolean with the proper 0/1 val!
valueForKey always returns an Objective-C object, even if the property has scalar type.
From the documentation (emphasis mine):
The default implementations of valueForKey: and setValue:forKey:
provide support for automatic object wrapping of the non-object data
types, both scalars and structs.
Once valueForKey: has determined the specific accessor method or
instance variable that is used to supply the value for the specified
key, it examines the return type or the data type. If the value to be
returned is not an object, an NSNumber or NSValue object is created
for that value and returned in its place.
The return value of your method is BOOL, which is defined as
typedef signed char BOOL;
on OS X and on the 32-bit iOS platform. So what valueForKey returns is a NSNumber
containing the result of
signed char val = [self.flags integerValue] & SomeConstantFlag;
and that can be in the range -128 .. 127.
To ensure that you get only YES or NO (aka 1 or 0) write your custom getter as:
-(BOOL)someConstantFlag
{
return ([self.flags integerValue] & SomeConstantFlag) != 0;
}
Remark: On the 64-bit iOS platform (but not on 64-bit OS X), BOOL is defined as the C99 _Bool, which is a "proper" boolean type and can take only the value 0 or 1.
NSNumber *value = #([model objectForKey:#"someConstantFlag"]);
BOOL boolVal = [value boolValue];
I think you should consider the following problems. Firstly, integerValue returns NSInteger which means if you support 64Bit architecture it will return int_64 not int_32, what is more in your code here
[self.flags integerValue] & SomeConstantFlag
this does the following if flags is 00010 and somConstantFlags is 00001 the & of those will do something you probably does not expect because you will get value of 00000 which equals 0 or if they are 00011 and 00110 you will get 00010 which equals 2. So that is why when you call valueForKey you get 2 or 4 or something else depending on your flags :)
What is more in objective-C everything different then 0 is YES.
Try reconsidering your bit logic :). See The following example
enum
{
kWhite = 0,
kBlue = 1 << 0,
kRed = 1 << 1,
kYellow = 1 << 2,
kBrown = 1 << 3,
};
typedef char ColorType;
and in your setter check the following
ColorType pinkColor = kWhite | kRed;
if (pinkColor & (kWhite | kBlue | kRed | kYellow)) {
// any of the flags has been set
}
The flags kWhite, kBlue, kRed and kYellow have been set.
However, kBrown has not been set.

CFNumber macro for constants

Pretty sure the answer is No on this one, but it's painful enough I have to ask: Is there a CFNumber equivalent to CFString's CFSTR macro? To avoid this sort of thing:
char one = 1;
CFNumberRef cfONE = CFNumberCreate(kCFAllocatorDefault, kCFNumberCharType, &one);
if (cfONE != NULL) {
... finally I can compare something to the number 1! ...
CFRelease(cfONE);
} else {
// not likely, but possible, if you really want to be conservative
}
Note that I'm not using Objective-C in this particular code.
Eric
If you plan on using this function multiple times, you could the static modifier and stop worrying about deallocation:
static CFNumberRef cfONE = NULL;
if (cfONE == NULL) {
static char one = 1;
cfONE = CFNumberCreate(kCFAllocatorDefault, kCFNumberCharType, &one);
assert (cfONE != NULL); // Oh no, destroy the world!
}
// ... finally I can compare something to the number 1! ...
So long as you have static, the static analyzer will leave you alone on the leak issue since it is an expected constant size memory allocation, O(1) rather than O(n) where n is the number of executions.
There are several ways to make this a macro. I came up with this lazy one:
#define CFNUMDEF(name, type, numberType, value) \
static CFNumberRef name = NULL; \
if ( name == NULL) { \
static type val = value ;\
name = CFNumberCreate(kCFAllocatorDefault, numberType , &val);\
assert ( name != NULL); \
}
CFNUMDEF(cfONE, char, kCFNumberCharType, 1);
// ... finally I can compare something to the number 1! ...
CFSTR is a little different from your case
CFSTR() allows creation of compile-time constant CFStringRefs; the argument
should be a constant C-string.
CFSTR(), not being a "Copy" or "Create" function, does not return a new
reference for you. So, you should not release the return value. This is
much like constant C or Pascal strings --- when you use "hello world"
in a program, you do not free it.
Where as the object you create with CFNumberCreate will be owned by the caller so you may still want to keep the word create in the name to make this clear.
You could always make a helper function just to avoid passing the same arguments over and over
CFNumberRef PSNumberCreateWithChar(x)
{
return CFNumberCreate(kCFAllocatorDefault, kCFNumberCharType, &x);
}
CFNumberRef and NSNumber are toll free bridged so you can use the NSNumber literal syntax and cast the result.

Setting Integer value in Objective c

I have recently started programming in iOS.. I am going through a code snippet that declares the following variables:
int rc = 0X00;
sqlite3_stmt *pStmt = 0X00;
FMStatement *stat = 0X00;
BOOL abc = 0X00;
what does this mean?? I read somewhere that setting 0X00 in a reference variable means setting it to NULL (in C). But what does setting a BOOL type variable and an int type variable to 0X00 mean??
I suggest you read up about the basics of programming languages, specifically, C programing with pointers. Objective-C is a superset of C and follows many similar rules.
But to your question:
The 0x in front of the literal values in the code (0x00) specifies that the value is interpreted as hexadecimal rather than decimal. But 0x00(hex) is the same as 0(dec).
int rc = 0x00; //same as int rc = 0;
int is a primitive type in both Obj-C and C that specifies an integer, effectively you are initializing the variable. In the C language you must initialize variables otherwise they could be pointing at a random piece of memory.
Therefore, examine this code:
int a;
int b = 0;
//a is NOT equal to b!
In C, the variable 'a' has not be initialized and therefore its not typically safe to assume that it will be initialized to 0. Always initialize your variable.
If you did a printf, or an NSLog of the variable 'a' you will see that it prints some huge number and it doesnt make sense (sometimes this is compiler dependent)
The same can be said for a BOOL. Although setting a BOOL to 0 is the same as setting it to false;
BOOL flag = 0; //The same as saying BOOL flag = false;
Now for the final part of your code:
FMStatement *stat = 0X00;
Often in Objective-C if you are dealing with pointers and objects you need to initialise the pointer to point at some memory address. The actual memory address is usually determined by the stack/heap and you don't need to worry about that. But you do need to ensure that the pointer isn't pointing to the wrong location (known as a garbage pointer).
To do this, we simply set our pointer to nil. eg:
FMStatement *stat = nil; //This pointer is now safe. Although memory still hasnt been allocated for it yet
This is usually taken care of for you though when you immediately allocate the memory for an object, therefore in this case you don't need to worry about initializing the pointer to nil:
FMStatement *stat = [[FMStatement alloc]init];
Like I said, I recommend you read about basic C programming, allocations, pointers, datatypes, initialising etc, once you have a grasp of this, then move to Objective-C which then builds ontop of it with Object-Oriented stuff.
Good luck.
0X00 is simply 0 in hexadecimal notation. So,
int rc = 0X00;
is the same as
int rc = 0;
Same for BOOL variables, where 0 is the same as NO. Using 0X00 is odd -- it'd make more sense to use 0 or NO where appropriate, and use nil for the pointers.

Resources