Objective-C const NSString * vs NSString * const - objective-c

I'm trying to a NSString constant in my .h file to be defined in my .m. I understand that
extern NSString * const variableName; in the .h and
NSString * const variableName = #"variableValue"; is the way to do this. Examining C tutorials I see that const is supposed to go before variable definitions. My question is why is it not declared as extern const NSString * variableName; in the .h and const NSString * variableName = #"variableValue"; in the .m. I know this doesn't work because I encounter compiler warnings which say 'Passing argument 1 of methodName: discards qualifiers from pointer target type'. What does this mean?

It's not the same. The const modifier can be applied to the value, or the pointer to the value.
int * const
A constant pointer (not modifiable) to an integer (its value can be modified)
const int *
A modifiable pointer to a constant integer (its value can't be modified)
So you can imagine:
const int * const;

Constant pointer is NOT a pointer to constant. Constant pointer means the pointer is constant. E.g. constant pointer int * const ptr2; indicates that ptr2 is a pointer which is constant. This means that ptr2 cannot be made to point to another integer. However, the integer pointed by ptr2 can be changed.
Whereas a pointer to constant const int * ptr1; indicates that ptr1 is a pointer that points to a constant integer. The integer is constant and cannot be changed. However, the pointer ptr1 can be made to point to some other integer.

Related

Ordering of Preprocessor statement gives me warning?

I have a code as following
ApplicationSetting.h
FOUNDATION_EXPORT BOOL *const TEST_MODE;
ApplicationSetting.m
#ifdef DEBUG
BOOL *const TEST_MODE = YES;
#else
BOOL *const TEST_MODE = NO;
#endif
The above .m file's code gives me this warning
Incompatible integer to pointer conversion initializing 'BOOL *const'
(aka 'signed char *const') with an expression of type 'signed char';
But, if I change it to be come like this
#ifdef DEBUG
BOOL *const TEST_MODE = NO;
#else
BOOL *const TEST_MODE = YES;
#endif
It works just fine without any warning.
Do you have any idea how could this happens?
You really meant to write a value:
FOUNDATION_EXPORT const BOOL TEST_MODE;
…BOOL is not an objc object, it is a signed char.
as far as the error, the compiler complains because you are assigning numeric values to the pointer value -- where only 0 (aka NULL) is acceptable to the compiler, and any other number (YES is 1) will produce the error/warning.
P.S. Just use bool.
Just to explain in more detail:
Incompatible integer to pointer conversion …
You tried to convert an integer value—a number—to a pointer. This can be done, but it's usually a bad idea and consequently requires a high level of explicitness. It's hard to do by accident (nowadays/on this compiler), and there are reasons for that.
… initializing 'BOOL *const' (aka 'signed char *const') …
This is the type of variable you declared. As this part of the message explains, BOOL is also known as signed char (i.e., the one is typedef'd to the other).
char is the smallest of the integer types in C, so you've declared this variable to hold a pointer to an integer.
… with an expression of type 'signed char';
The expression in this case is the initializer from your declaration. It's the part that you changed between the two versions of the declaration: YES in one case, NO in the other.
The Objective-C headers define NO as 0 and YES as 1, both cast to BOOL (which, as noted above, is defined as signed char).
So:
Your initializer is a BOOL value (as justin rightly pointed out, BOOL with no *), which is an integer.
Your variable holds a BOOL *—a pointer.
The compiler will not let this fly without you being very explicit that this is something you mean to do.
Even if you did convince the compiler to go along with this, it would not be correct code.
As justin already established, you should leave out the *. This will declare the variable as holding a BOOL value, not a pointer.
I also second his suggestion of using bool instead. Unlike BOOL, a bool can never be any value except true (1) or false (0), unless you try very hard.

pointer to int conversion compiler trouble?

So I'm using Xcode to write a few tiny Objective-C apps and I have the line of code:
int * foo;
foo = 5;
NSLog(#"does it work... %i", foo);
Now it compiles and runs just fine, but I was wondering, why does it give me the warning, "Incompatible integer to pointer assigning..."? I thought the code above tells it to set whatever foo is pointing to to 5, not to set the pointer itself to 5. Can anyone help me out with this?
This piece of code works by accident: the int pointer happens to have sufficient space to hold an integer value, and NSLog reinterprets the pointer as an integer, but the program is still incorrect.
A pointer to int should be assigned an address of an integer variable, like this:
int *foo;
int var;
foo = &var;
Now you can assign the variable through the pointer:
*foo = 5;
You can also read the value through the pointer or through the variable:
NSLog(#"%d %d", *foo, var);

What is the difference of the following const definition

Usually I use the first one to define const, but I don't know the difference of the following clearly.
static NSString* kFetcherCallbackThreadKey = #"_callbackThread";
static NSString* const kFetcherCallbackRunLoopModesKey = #"_runLoopModes";
NSString* const kFetcherRetryInvocationKey = #"_retryInvocation";
static const NSUInteger kMaxNumberOfNextLinksFollowed = 25;
In C, the static keyword, used outside a function, is used to declare a symbol that will be accessible only from the file in which it's declared. Kind of «private» global variables.
The const keyword means «constant». Read, the value can't be modified.
Note the two statements are different:
const int * x;
int * const x;
The first one defines a pointer to a constant integer (its value can't be modified, but it can point to something else).
The second one defines a constant pointer to an integer (the pointer value can't be modified, but the value of the int may be).
So you can perfectly have:
const int * const x;
So in your case:
static NSString* kFetcherCallbackThreadKey = #"_callbackThread";
A pointer to a NSString instance that will be accessible only from the file in which it's declared.
static NSString* const kFetcherCallbackRunLoopModesKey = #"_runLoopModes";
A constant pointer to a NSString instance that will be accessible only from the file in which it's declared.
NSString* const kFetcherRetryInvocationKey = #"_retryInvocation";
A constant pointer to a NSString instance that may be accessed from other files of your project.
static const NSUInteger kMaxNumberOfNextLinksFollowed = 25;
A constant integer that will be accessible only from the file in which it's declared.
static means that the variable is accessible only within the compilation unit it's declared in - essentially this source file. const means its value can never change. You can use one, both, or none depending on what you're looking for.
This is a static string which will be the same reference for all instances of the class (static). If you change it in one instance, it will change in all other instances.
static NSString* kFetcherCallbackThreadKey = #"_callbackThread";
This is an NSString pointer to a constant object that is also shared between all instances (static). The const directive makes the variable immutable.
static NSString* const kFetcherCallbackRunLoopModesKey = #"_runLoopModes";
This is a pointer to a constant NSString object. It could have a different instance for each class (if NSStrings are not interned by the compiler, I'm not sure if they are), but cannot be changed (const).
NSString* const kFetcherRetryInvocationKey = #"_retryInvocation";
This is a constant static integer. It will be shared between all instances of the class (static) and cannot be changed (const).
static const NSUInteger kMaxNumberOfNextLinksFollowed = 25;

Does const go before or after CGFloat?

Does it even matter? Const before or const after? I'm guessing that whether I put const before or after CGFloat it makes the value of CGFloat constant, but what about the pointer? Is this right for Objective-C:
// Example.h
extern CGFloat const kPasscodeInputBoxWidth;
// Example.m
CGFloat const kPasscodeInputBoxWidth = 61.0f;
It can go either before or after. In the case of a pointer, what matters is whether the const ends up before or after the asterisk:
const int *a; // pointer to const int -- can't change what a points at
int const *a; // same
int *const a; // const pointer to int -- can't change the pointer itself.
// Note: must be initialized, since it can't be assigned.
It doesn't matter (I've always used the former, but I guess it's a matter of style):
const CGFloat kPasscodeInputBoxWidth = 61.0;
CGFloat const kPasscodeInputBoxWidth = 61.0;
At least in the current rendition of CGFloat, it's just a typedef of double, so do as you would with a regular primitive datatype. For pointers, the placement of const will determine if it's the pointer or the value that is constant, so for that it does matter.

Attribute introspection to get a variable in Objective-C

Given a variable id x and a string NSString *s how can I get the instance attribute with name s for variable x?
ie. If we write NSString *s=#"a", then we want x.a
The Objective-C Runtime Reference lists
Ivar class_getInstanceVariable(Class cls, const char * name)
which returns an opaque type representing an instance variable in a class. You then pass that to
id object_getIvar(id object, Ivar ivar)
to get the actual instance variable. So you could say
#import <objc/runtime.h>
id getInstanceVariable(id x, NSString * s)
{
Ivar ivar = class_getInstanceVariable([x class], [s UTF8String]);
return object_getIvar(x, ivar);
}
if the instance variable is an object. However, if the instance variable is not an object, call
Ivar object_getInstanceVariable(id obj, const char * name, void ** outValue)
passing in a pointer to a variable of the right type. For example, if the instance variable is an int,
int num;
object_getInstanceVariable(x, [s UTF8String], (void**)&num);
will set num to the value of the integer instance variable.
Providing that x is key-value coding compliant for the a property, you can just do this:
id result = [x valueForKey:s]