What is the significance of the positioning of the
const
keyword when declaring a variable in Objective-C, for example:
extern const NSString * MY_CONSTANT;
versus
extern NSString * const MY_CONSTANT;
Using the first version in assignments produces warnings about "qualifiers from pointer target type" being discarded so I'm assuming that the second version ensures that the pointer address remains the same. I would really appreciate a more definitive answer though. Many thanks in advance!
In the first case, you are declaring a mutable pointer to an immutable const NSString object, whereas in the second case, you are declaring an immutable pointer to a mutable NSString object.
An easy way to remember this is to look at where the * is situated; everything to the left of it is the "pointee" type, and everything to the right of it describes the properties of the pointer.
extern const NSString * MY_CONSTANT;
- Pointer is variable , but the data pointed by pointer is constant.
extern NSString * const MY_CONSTANT;
- Pointer constant , but the data pointed by pointer is not constant.
In general, const always applies to the token just before the const. In the second case, the const means that the pointer is a constant, not the thing pointed at. The exception is when the const appears before anything that can meaningfully be constant, as in your first example. In this case it applies to the first type, in this case NSString, so its equivalent to extern NSString const * MY_CONSTANT
Related
When reading the headers of Foundation I found this:
- (__strong const char *)UTF8String NS_RETURNS_INNER_POINTER;
// Convenience to return null-terminated UTF8 representation
This is from NSString.h in the iOS 7.1 SDK, what does __strong const char * mean here?
I'm most confused about the "__strong" here.
Foundation is shared between iOS and Mac OS. On Mac OS, for a while there existed a garbage collection memory management system. It's now deprecated and not longer supported on Mac OS. It was never used on iOS.
GC used __strong as a modifier on plain pointer type declarations to make the pointed to memory collectible. This usage of __strong has no meaning in ARC or manual retain/release code. The fact that there's no warning for the declaration is probably only because clang issues no warnings in system headers.
NS_RETURNS_INNER_POINTER is a Clang annotation that indicates the method returns a pointer to one of its inner data structures (i.e., an instance variable), and that inner variable is not reference-counted, so whenever that method is called, ARC should increment the retain count for the receiver (because that object should not be released as long as the pointer to its inner data structures is in use).
In this case, __strong essentially acts in conjunction with NS_RETURNS_INNER_POINTER to indicate the lifetime of an object that returned a pointer to its inner data structures.
__strong means that the object (of class NSString in this case), that holds the pointer, retains its ownership till the end of the life of the object and the pointed memory will be garbage-collected. Hence INNER.
const char * means that the char array this pointer points to is constant, i.e. cannot be changed for example by changing some characters in it (you will get a compiler error).
If you try to compile this:
#import <Foundation/Foundation.h>
int main(int argc, char **argv)
{
NSString *s = #"string";
const char *str = [s UTF8String];
printf("%s\n", str);
str[0] = 'S';
printf("%s\n", str);
return 0;
}
you'll get read-only variable is not assignable error.
After updating Xcode to version 5.1, I had a warning that told me I had defined a constant that I wasn't using. Its definition looked like this:
static NSInteger const ABCMyInteger = 3;
I was happy to see that it got marked, because I thought this meant that the compiler was now able to check for unused constants in addition local to variables.
I refactored some more, making three NSString constants obsolete. All three were defined very similarly to the NSInteger from above:
static NSString *const ABCMyString = #"ABCMyString";
To my surprise, however, these do not get marked as "unused", though I know for sure that they aren't used anymore.
Can someone explain why an NSInteger does get noticed by the compiler as unused, but an NSString does not?
A primitive variable is just a memory block allocated in a static memory part and initialized by the compiler. The string object, however, is a variable initialized at runtime (in startup, probably), so the compiler adds an implicit call to the constructor and uses the variable as a parameter for that call. So the variable is being used.
The _unused item of the structure is IMHO not a directive, but just a member variable, probably it is added for better alignment (fills the object size to a round size).
The definition of an NSString literal at compile time rely on the use of the NSSimpleCString meta class.
This class looks something like this:
#interface NSSimpleCString : NSString {
#package
char *bytes;
int numBytes;
#if __LP64__
int _unused;
#endif
}
#end
#interface NSConstantString : NSSimpleCString
#end
The addition of the _unused flag make me believe that further down the implementation of NSSimpleCString the code will instruct the compiler to silence those warnings with __unused.
You can try yourself by prepending your integer or float constant with __unused like:
__unused static const NSInteger ABCMyInteger = 3;
For a more in depth explanation read the article on literals by Mike Ash
What is the significance of the positioning of the
const
keyword when declaring a variable in Objective-C, for example:
extern const NSString * MY_CONSTANT;
versus
extern NSString * const MY_CONSTANT;
Using the first version in assignments produces warnings about "qualifiers from pointer target type" being discarded so I'm assuming that the second version ensures that the pointer address remains the same. I would really appreciate a more definitive answer though. Many thanks in advance!
In the first case, you are declaring a mutable pointer to an immutable const NSString object, whereas in the second case, you are declaring an immutable pointer to a mutable NSString object.
An easy way to remember this is to look at where the * is situated; everything to the left of it is the "pointee" type, and everything to the right of it describes the properties of the pointer.
extern const NSString * MY_CONSTANT;
- Pointer is variable , but the data pointed by pointer is constant.
extern NSString * const MY_CONSTANT;
- Pointer constant , but the data pointed by pointer is not constant.
In general, const always applies to the token just before the const. In the second case, the const means that the pointer is a constant, not the thing pointed at. The exception is when the const appears before anything that can meaningfully be constant, as in your first example. In this case it applies to the first type, in this case NSString, so its equivalent to extern NSString const * MY_CONSTANT
If I have a C-string like this:
const char* str= "hello";
I know well that I can't change any character of the string because the pointer is const.
But if I have this:
- (CLLocationDistance)distanceFromLocation:(const CLLocation *)location
I can still mutate the object state using it's methods.
If the pointer is to a NSMutableString, I'm still able to mutate it.
Then what does the const stand for?
In that method declaration, location is a pointer to a constant CLLocation. But when you send a message to the location object, the const-ness is not preserved; the method that handles the message sees self as a non-const object. (Note that this is different than C++, which supports const member functions where this is a pointer to a constant object.)
So the const in that declaration is not particularly useful. Perhaps it was written by someone used to the C++ way of doing things.
When you do see const attached to an object pointer in Objective-C, it is usually like this:
extern NSString * const SomeConstantString;
This declares SomeConstantString as a constant pointer to some non-constant object (in this case, an NSString). The pointer itself is constant, so your program can't change SomeConstantString to point to some other NSString object.
I know well that I can't change any character of the string because
the pointer is const.
No, the pointer is mutable. The characters it points to are const.
I can still mutate the object state using it's methods.
There is no const-correctness for Objective-C objects like there is in C++. The compiler does not care which messages (mutating or not) you send to a const object. So there's no sense in declaring a pointer to a const object. The cited framework method is an anomaly, probably an oversight.
Mind the difference:
// constant pointer
char * const str = "";
// pointer to constant (two equivalent ways)
const char * str = "";
char const * str = "";
The keyword const applies applies to whatever is immediately to its left. If there is nothing to its left, it applies to whatever is immediately to its right.
In Objective-C all method parameters are always passed by value. This includes primitives, structs, unions, and pointers, and any other made up type.
Note that you can't have variables of type object. A expression like NSObject o; produces a compiler error with message "Interface type cannot be statically allocated".
The only way to pass an object is passing a pointer. The pointer is passed as value, but lets the code inside the method reference the object and change it. So in a way, it is as if you are passing the object by reference (in reality you are passing the pointer by value).
When compiling an Objective-C program, the methods are turned into C functions, and each "message send" (aka "method call", though it isn't exactly the same) is ran using the runtime function objc_sendMsg. This function doesn't know or care if you qualified the object with const or not. If you want an immutable object, you have to code that immutability inside the object. Example:
// const qualifying an object is ignored whether in variables or method arguments:
const NSMutableArray *array = [NSMutableArray new]; // const is ignored
-(void)someMethod:(const NSMutableArray *)array { ... // const is ignored
// calling the methods that mutate the object works fine
[array removeAllObjects];
I'm using NSUSerDefaults to store user preferences. I remember reading somewhere that setting the keys as constants is a good idea - and I agree. The following code is what I currently have:
[[NSUserDefaults standardUserDefaults]
setObject:[NSNumber numberWithInt:polygon.numberOfSides]
forKey:#"polygonNumberOfSides"];
I tried changing this to:
#implementation Controller
NSString const *kPolygonNumberOfSides = #"polygonNumberOfSides";
-(void)savePolygonInfo {
[[NSUserDefaults standardUserDefaults]
setObject:[NSNumber numberWithInt:polygon.numberOfSides]
forKey:kPolygonNumberOfSides];
}
While this does work, it produces "warning: passing argument 1 of 'objectForKey:' discards qualifiers from pointer target type". I'm keen to keep my code free from compiler warnings. How can I fix this warning?
You should use:
NSString * const kPolygonNumberOfSides = #"..."; // const pointer
instead of:
NSString const * kPolygonNumberOfSides = #"..."; // pointer to const
The first is a constant pointer to an NSString object, while the second is a pointer to a constant NSString object.
It is a subtle difference. The compiler warning happens because setObject:forKey: is declared as follows:
- (void)setObject:(id)value forKey:(NSString *)defaultName;
It is expecting the defaultName argument to be of type NSString *. When you instead pass in a pointer to a constant, you've given it something different.
Update: I want to point out that these constants should be defined as static if they are only going to be used from within a single file. I say this because I have run across this problem myself: if you do not declare them as static, then they will exist in the global namespace, and you will not be able to use a variable with the same the same name in another file. see Constants in Objective-C for more information. To explain by example, this is what I currently use for keys that I only need to use in one .m file:
static NSString * const kSomeLabel = #"...";
Don't use const with Objective-C objects, they weren't really designed to use it. NSString objects (among many others) are already immutable by default by virtue of their design, so making them const is useless.
As e.James suggested, you can use an NSString * const, which is a constant pointer to an NSString. This is subtly different from a const NSString * (equivalent to NSString const *), which is a pointer to a constant NSString. Using a NSString * const prevents you from reassigning kPoly to point to a new NSString object.
For access from other classes:
.h
extern NSString * const PolygonNumberOfSidesPrefsKey;
.m
NSString * const PolygonNumberOfSidesPrefsKey = #"PolygonNumberOfSides"
For access only inside current class:
.m
static NSString * const kPolygonNumberOfSidesPrefsKey = #"PolygonNumberOfSides"
I would suggest even making the constant more descriptive. A constant for the number of sides of a polygon could come from anywhere. As a suggestion, how about:
kDefaultsPolygonNumberOfSides;
instead.