What is the difference of the following const definition - objective-c

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;

Related

objective-c constants.h static const

I googled a lot of similar questions, but none of them had a really good answer about what i needed.
Im wondering about the best way to write constants in some .h file.
my constants file is just a clear file with no imports, just clear .h file. I import it in prefix file.
when i use
static NSInteger someInteger = 1;
or
static BOOl someBool = YES;
The compiler compiles okay but gives me a warning that this variable is unused even though im using it multiple times in different classes.
But if i use
static NSString* someString = #"";
there are not any warnings.
also if i use
const NSInteger someInteger = 1;
Compiler compiles okay for a real device, but when running on a simulator it does not compile with an error duplicate symbols for architecture i386
also what is the difference between
const NSString* someString = #"";
const NSInteger someInteger = 1;
and
NSString* const someString = #"";
NSInteger const someInteger = 1;
I ended up using static const NSInteger someInteger =1;, but i wonder if this is a right option.
So really my question is: what words should i use to successfully create a constants.h file?
For all types (both primitive or otherwise) then you need to provide a single implementation of it somewhere:
constants.h:
extern const int someValue;
extern NSString * const someString;
constants.m:
const NSInteger someValue = 1;
NSString * const someString = #"Some string";
You never want to use static variables in header files as you will end up with multiple copies of the "constant" in every implementation file that includes that header (they may not upset the linker, and cause a link error, but they are there).

objective c require parameter to be a constant

So I'm new to objective C, and typed languages in general, although I am enjoying their verbosity.
I am defining a bunch of constants like this:
NSString const *MAP_TILES_TYPE_IDX = #"idx";
NSString const *MAP_TILES_TYPE_MLS = #"mls";
NSString const *MAP_TILES_TYPE_PROPERTY = #"mu";
NSString const *MAP_TILES_TYPE_SERVICES = #"sr";
and I have a method that should only accept one of these strings:
-(void) addTileLayer:(NSString *)type {
...
}
Can anybody give me a suggestion to better enforce the fact that I want the parameter type to be one of my defined constants?
I'm open to any suggestion.
You should replace NSString with an enum, like this:
typedef enum {
MAP_TILES_TYPE_IDX,
MAP_TILES_TYPE_MLS,
MAP_TILES_TYPE_PROPERTY,
MAP_TILES_TYPE_SERVICES
} MapTilesType;
...
-(void) addTileLayer:(MapTilesType)type {
...
}
If you need an NSString for these constants, define an NSArray that maps enum values to strings, like this:
NSArray *tileTypeToTypeName = #[#"idx", #"mls", #"mu", #"sr"];
If you require a method to accept only certain strings, why not make it take an enum instead of NSString as an argument and internally translate the enum to a string that you need?
This way users of the method would be limited to enum values only. Plus you can hide the actual strings from them (not always needed, but looks cleaner).

Objective-C const NSString * vs NSString * const

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.

What is the best way to define string constants in an objective-c protocol?

I have defined a protocol that all my plug-ins must implement. I would also like the plug-ins to all use certain strings, like MyPluginErrorDomain. With integers this is quite easily achieved in an enum, but I can't figure out how to do the same with strings. Normally, in classes I would define
extern NSString * const MyPluginErrorDomain;
in the .h file and in the .m file:
NSString * const MyPluginErrorDomain = #"MyPluginErrorDomain";
but that doesn't work very well in a protocol, because then each plug-in would have to provide its own implementation which defeats the purpose of having a constant.
I then tried
#define MYPLUGIN_ERROR_DOMAIN #"MyPluginErrorDomain"
but the implementing classes in the plug-in can't seem to see the #define. Who knows a good solution?
You can declare them in the header with the protocol (but outside the protocol interface itself), then define them in an implementation file for the protocol (obviously it wouldn't have an #implementation section - just your NSString definitions).
Or have a separate .h/.m pair that is just for the string constants (the protocol header can import the string constants header).
You keep the .h definition:
extern NSString * const MyPluginErrorDomain;
but put this part into a separate .m file that gets included in your framework:
NSString * const MyPluginErrorDomain = #"MyPluginErrorDomain";
So plug-ins can still implement the interface but when compiling they link or compile in your other .m file, so they will see the value of MyPluginErrorDomain.
In C++, I would declare them in a header like this:
const char * const MYPLUGIN_ERROR_DOMAIN = "MyPluginErrorDomain";
const char * const MYPLUGIN_FOO_DOMAIN = "MyPluginFooDomain";
Note that as the pointers are const, they will be local to the translation units the header is #included in, and so there will be no need to use extern to prevent multiple definition errors.
You should implement it as extern strings as in your example:
extern NSString * const MyPluginErrorDomain;
or provide extern functions which return static storage data. For example:
/* h */
extern NSString * MyPluginErrorDomain();
/* m */
NSString * MyPluginErrorDomain() {
static NSString * const s = #"MyPluginErrorDomain";
return s;
}
The reason is that strings and keys are often used and compared by pointer value or hash value, rather than true string comparison (isEqualToString:).
At the implementation level, there is a big difference between:
In code, that means that when the strings compared are defined in multiple binaries:
Say 'MyPluginErrorDomain' and 'key' have identical string values, but are defined in different binaries (i.e. on in the plugin host, one in the plugin).
/////// Pointer comparison (NSString)
BOOL a = [MyPluginErrorDomain isEqualToString:key];
BOOL b = MyPluginErrorDomain == key;
// c may be false because a may be true, in that they represent the same character sequence, but do not point to the same object
BOOL c = a == b;
/////// Hash use (NSString)
// This is true
BOOL d = [MyPluginErrorDomain hash] == [key hash];
// This is indicative if true
BOOL e = [MyPluginErrorDomain hash] == [someOtherStringKey hash];
// because
BOOL f = [MyPluginErrorDomain isEqualToString:someOtherStringKey];
// g may be false (though the hash code is 'generally' correct)
BOOL g = e == f;
It is therefore necessary to provide the keys in many cases. It may seem like a trivial point, but it is hard to diagnose some of the problems associated with the difference.
Hash codes and pointer comparisons are used throughout Foundation and other objc technologies in the internals of dictionary storage, key value coding... If your dictionary is going straight out to xml, that's one thing, but runtime use is another and there are a few caveats in the implementation and runtime details.

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]