I am taking a Constants.h file for saving all my values. And i declare like this :
static NSString *NAME = #"Name";
static NSString *MAILID = #"Mail-id";
static NSString *ADDRESS = #"Address";
I also use this key in my code. But always its showing a warning Unused variable "NAME"
If i take
const NSString *NAME = #"Name";
Then its showing Error. So please anybody can help me, how can i resolve this warning ?
Thanks :)
In C, and therefore in Objective-C, a static is a variable with file-level scope. Both #import and #include have the same effect as copying and pasting the header file's text into your source file. So with static, each file that includes the header is getting its own personal copy of the symbols NAME, MAILID, etc. If you don't use one the compiler will therefore warn you.
const means constant. If used before the asterisk then it means a constant NSString, which doesn't have a lot of semantic sense because NSString is an opaque type. If you just use const you're also having every file that includes the header redeclare the variables. So you'll get a liker error.
What you probably want in the header is:
extern NSString *const NAME;
That says: (i) estern — not here, but in another unit; and (ii) NSString *const — it is a pointer to NSString and the address within the pointer never changes.
Then in any single implementation file add:
NSString *const NAME = #"NAME";
As otherwise every single compilation unit will sit back and expect one of the others to define it. You'll therefore get a linker error that the symbol is undefined.
Related
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
if you don't take #define into account, there are two notorious ways for declaring constants in objective-c project:
// Util.h
extern NSString * const MyConstant;
// Util.m
NSString * const MyConstant = #"value";
or the other directly in header file
// Util.h
static NSString *const MyConstant = #"value";
Now, there comes two questions:
1)
Both works, the second method is quite convenient, as I have only one place for editing values.
However as I saw from Apple .h files, the first method is always preferred, and I wonder if there are any downside, with the static method.
2) Looking at Apple docs, we often encounter very long constant name like: NSTextInputContextKeyboardSelectionDidChangeNotification. In the case you used a long constant name like that, what convention would you generally use to assign a value. If I want to use something descriptive I could use #"nsTextInputContextKeyboardSelectionDidChangeNotification", but sounds little odd.
I won't explain you all the specifics of the two types of constant declaration - in short:
The first one separates constant declaration from constant definition. Only declaration is included by header files. It also conveniently hides the actual value of the constant.
The second one is more problematic - the header file contains both declaration and definition of the constant. That means that whenewer you include the header, the constant is created again. I don't think this will work correctly when included from multiple files.
The second question - no problem with long constant names. Your example is a bit extreme but there is nothing wrong with it.
EDIT: Adding more information about the static NSString* const in header.
Let's have a header A.h:
//A.h
static NSString *const MyConstant = #"value";
and file including it A.m
//A.m
#import "A.h"
printA() {
NSLog(#"A.h Constant: %#", MyConstant);
}
First note that headers are removed by the preprocessor before compilation. That means that before compilation there won't be any A.h file and A.m will look like this:
//A.m
static NSString *const MyConstant = #"value";
printA() {
NSLog(#"A.h Constant: %#", MyConstant);
}
Let's create another constant header B.h and implementation file B.m with exactly the same contents:
//B.h
static NSString *const MyConstant = #"value2";
//B.m
#import "B.h"
printB() {
NSLog(#"B.h Constant: %#", MyConstant);
}
Note that the constant is declared twice, with the same name and different values. This is possible because static makes the constant private for the file that includes it. If you remove the static, you'll get a compilation error because the compiler will find two public global constants with the same name.
In theory it is possible to use static NSString* const in header files and everything will work correctly but as you can see, it doesn't do exactly what you want and can be a source of difficult-to-find bugs. That's why you should use static only from implementation files.
Just to add - the second way that you have shown doesn't make much sense.
If you are going to declare and define a constant in one place such as this, it's usually done in the .m file and not the .h file.
You do it this way for constants that are only going to be used in the class itself and not exposed.
I'm trying to instanciate a constant NSString by concatanating other NSString instances.
Here is what I'm doing in my implementation file :
static NSString *const MY_CONST = #"TEST";
static NSString *const MY_CONCATENATE_CONST = [NSString stringWithFormat:#"STRING %#", MY_CONST];
It leads to the following compilation error : Initializer element is not constant
I suppose this is because stringWithFormat doesn't return a constant NSString, but since there is no other way to concatenate strings in Obj-C, what am I supposed to do ?
Thanks for your help,
Eric.
I thought there must be a way to do this but the best I could do was using a #define directive. For example,
// Define the base url as an NSString
#define BASE_URL #"http://www.milhouse.co.uk/"
// Now the derived strings glued by magic
NSString *const kBaseURL = BASE_URL;
NSString *const kStatusURL = BASE_URL #"status.html";
NSString *const kBalanceURL = BASE_URL #"balance.html";
static const objects value is determined at compile-time so you indeed cannot add any method calls to their initialization. As an alternative you can do the following:
static NSString *const MY_CONST = #"TEST";
static NSString *MY_CONCATENATE_CONST = nil;
if (nil == MY_CONCATENATE_CONST)
MY_CONCATENATE_CONST = [NSString stringWithFormat:#"STRING %#", MY_CONST];
I think you need to step back and think about if the string needs to be defined as a const.
Clearly the string isn't a constant since you are trying to assign a new value to it - and that is not possible since you specifically instructed the compiler to make sure the value wasn't changed by using the const keyword.
If the string resides as a property in a class you could make it a read-only property - i.e. accessor method but no setter method. You would then be able to construct your string as you wish in the class internally while keeping the callers from changing the value.
In Objective-C, I can write:
id pString = #"Hello, World.";
and the compiler will instantiate an NSString without me needing to explicitly call a factory method. However, NSString is really just a Foundation class and thus presumably not part of the actual Objective-C language definition.
So when I write #"String", how does the compiler know to build an NSString in particular, and not some other string-like object? In other words, where does the Objective-C language stop and the Foundation library start?
When you write Objective-C code outside of Cocoa or GNUStep environments, #"..." is not linked to NSString.
In this case, gcc provides an option for specifying a class associated to literal strings:
-fconstant-string-class=class-name
Use class-name as the name of the class to instantiate for each
literal string specified with the syntax "#"..."". The default
class name is "NXConstantString".
The #"" directive appears to be built-in to the objective-c compiler.
For instance, if you remove all #imports from your .m source file (& prefix header), the following line will be a syntax error:
NSString *string = #"ABCD"; // (Doesn't know anything about NSString class)
However, if you change the Foundation NSString type to the built-in void type, it will compile just fine:
void *string = #"ABCD";
So, even without Foundation's NSString definition, the compiler knows how to turn #"" into something that can become an NSString instance at runtime (it probably won't instantiate without Foundation, but the compiler doesn't seem to mind); Since it accepts the syntax without needing any external library definitions, the compiler sees #"" as part of the language.
Your code, however, won't be able to make use of any #"" instance without importing Foundation.h, so from the point of view of your program, #"" is part of the library.
Another interesting bit is that an Objective-C string literal (#"" notation) returns the same type as an explicitly instantiated NSString object:
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
printf("string literal class: %s\n", object_getClassName(#"a string literal"););
NSString *str = [[NSString alloc] initWithUTF8String:"asdf"];
printf("explicit NSString class: %s", object_getClassName(str));
[pool drain];
return 0;
}
I vaguely remember that in other, older implementations of Objective-C, the string literal actually returned an object of a slightly different class, but that could be used interchangeably with NSString/NSCFString. Not totally sure on that part, though.
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.