NSString constant pointer to constant pointer - objective-c

I have a global constant:
FOUNDATION_EXPORT NSString *const ENGModelItemText; // .h file
NSString *const XYZConstant1 = #"XYZConstant1"; // .m file
... and I would like to create XYZConstant2 that would point to XYZConstant1. I thought it would be as simple as this:
NSString *const XYZConstant2 = &XYZConstant1
I played with * and & a bit but can't get it right. I'd like to get rid of #define for XYZConstant2 that I use now.

You cannot create a compile-time alias like this in C (and therefore in ObjC). You can create a runtime alias by declaring XYZConstant2 inside of a function or method, but not as a static. Compare this pure C, which creates the same error:
const char * const foo;
const char * const bar = foo;
(See also Compiler error: "initializer element is not a compile-time constant".)
Typically when this kind of aliasing is required (usually because a string constant was renamed), you use a #define (much as I hate defines).
That said, you should not rely on the fact that two object pointers are the same address unless you mean "it is this object" rather than "it has this value." (And you never mean that for strings because strings only have value.) Write to the semantics, not the implementation details. Don't prematurely optimize comparisons.

this is a constant, so compile time. If you want to point it, you can't with a constant.

Related

ObjC ternary operator and const strings

If you have a static const string, then its usage may cause inconsistent interpretation by the compiler. For example in this case:
const NSString* kUntitled = #"Untitled";
NSString* title = kUntitled;
the compiler will complain about assigning a const pointer to a non-const one ("discards qualifiers") and probably rightly so. This can be solved by either not using const at all, or by invoking kUntitled.copy (I somehow don't like the idea of typecasting (NSString*)kUntitled)
However, if you have instead:
NSString* title = aTitle ?: kUntitled;
then the compiler doesn't complain.
My first question is, can the warning be ignored in the first example? Are there any potential dangers in assigning a const NSString to a non-const one?
Second is, why does the compiler ignore the case with the ternary operator?
Welcome to the Weird and Wonderful World of C Declarations - the stuff of quiz questions ;-)
const NSString* kUntitled = #"Untitled";
You probably haven't written what you intended here. This defines kUntitled to be a mutable pointer to a "constant" string - usually referred to as a "pointer to a constant"... However it's "constant" for a reason as despite the common "pointer to a constant" it is actually a "read-only pointer" meaning you can read but not write via the pointer, what is pointed at might well be mutable but it is not mutable via this point if so...
Confused? What the above all means is that you can later write:
kUntitled = #"oops you probably thought you couldn't assign";
As the pointer itself is mutable, it can be changed to point at other things.
What you probably intended was:
NSString * const kUntitled = #"Untitled";
which declares a constant pointer to a string - it is the pointer itself which cannot be changed so:
kUntitled = #"this will produce a compile error, can't change a constant";
If you use this version of the declaration then you won't get an error on your assignments:
NSString* title = kUntitled;
NSString* title = aTitle ?: kUntitled;
However that still leaves the question of why you didn't get an error from the second with your original declaration...
The RHS of the assignment, aTitle ?: kUntitled is actually valid, the weird world of C again. This expression is just shorthand for aTitle ? aTitle : kUntitled and the rules for this operator in C state that the second and third arguments can be of the same base pointer type, NSString * in your case, but differ in qualifiers, const in your case, and the resultant type is the base pointer type with all the qualifiers of the two operands.
In other words the result of this expression is treated as const NSString *. Which means you should get the same warning as for the first assignment.
It appears that the compiler is treating the operator as though the resultant type is the base pointer type with none or only the common qualifiers of the two operands - i.e. the opposite of the definition.
So for the second problem you may have found a compiler bug, you should report it to Apple (bug reporter.apple.com) and see what they say.
HTH
The warning is a side effect of the fact that const NSString * kUntitled is incorrect. This is a declaration of a pointer-to-readonly-NSString. Note the placement of the "read only" there -- it's referring to the string, not the pointer. But ObjC objects are never read only, never const. You may say that literal NSStrings are, of course, but that's implementation dependent, and even modifiable in some runtime environments.
Thus you can never correctly assign this object anywhere else (unless that variable also was a pointer to a const object).
The declaration that you should be using is NSString * const kUntitled -- this is "readonly-pointer-to-NSString", i.e., the pointer cannot be changed to point at another object.

Objective-C Nullability: Qualifying Constant Strings

I have gotten into a pretty good habit of declaring and using constant strings for things like NSNotification names. I declare them like so:
extern NSString * const ABCAwesomeThingHappenedNotification;
With the introduction of Xcode 6.3 and Swift 1.2, I'm going back and auditing Objective-C classes that interop with Swift using the new nonnull, nullable, and null_unspecified qualifiers.
When adding the qualifiers to a header that also has externally visible static strings, I receive the following warning:
warning: pointer is missing a nullability type specifier (__nonnull or __nullable)
Hmm. That's confusing / interesting. Can someone explain the reasoning behind this message? When using ABCAwesomeThingHappenedNotification in Swift, it never suggests that it's an optional String or implicitly unwrapped String.
I agree that having this specifier shouldn't be required but here is syntax
extern NSString * const MyConstant;
extern NSString * __nonnull const MyConstant;
In your implementation, you could define:
NSString * const ABCAwesomeThingHappenedNotification = #"ABCAwesomeThingHappenedNotification";
in which case the pointer is clearly nonnull. However, this is also valid:
NSString * const ABCAwesomeThingHappenedNotification = nil;
which must be considered nullable because the pointer is always a null pointer.
(The explicit initialisation to nil is redundant since this happens implicitly if no initial value is provided, but clarifies this example.)

When to use static string vs. #define

I am a little confused as to when it's best to use:
static NSString *AppQuitGracefullyKey = #"AppQuitGracefully";
instead of
#define AppQuitGracefullyKey #"AppQuitGracefully"
I've seen questions like this for C or C++, and I think what's different here is that this is specifically for Objective C, utilizing an object, and on a device like the iPhone, there may be stack, code space or memory issues that I don't yet grasp.
One usage would be:
appQuitGracefully = [[NSUserDefaults standardUserDefaults] integerForKey: AppQuitGracefullyKey];
Or it is just a matter of style?
Thanks.
If you use a static, the compiler will embed exactly one copy of the string in your binary and just pass pointers to that string around, resulting in more compact binaries. If you use a #define, there will be a separate copy of the string stored in the source on each use. Constant string coalescing will handle many of the dups but you're making the linker work harder for no reason.
See "static const" vs "#define" vs "enum". The main advantage of static is type safety.
Other than that, the #define approach introduces a flexibility of inline string concatenation which cannot be done with static variables, e.g.
#define ROOT_PATH #"/System/Library/Frameworks"
[[NSBundle bundleWithPath:ROOT_PATH#"/UIKit.framework"] load];
but this is probably not a good style :).
I actually would recommend neither, you should use extern instead. Objective-c already defines FOUNDATION_EXPORT which is more portable than extern, so a global NSString instance would look something like this:
.h
FOUNDATION_EXPORT NSString * const AppQuitGracefullyKey;
.m
NSString * const AppQuitGracefullyKey = #"AppQuitGracefully";
I usually put these in declaration files (such as MyProjectDecl.h) and import whenever I need.
There are a few differences to these approaches:
#define has several downsides, such as not being type safe. It is true that there are workarounds for that (such as #define ((int)1)) but what's the point? And besides, there are debugging disadvantages to that approach. Compilers prefer constants. See this discussion.
static globals are visible in the file they are declared.
extern makes the variable visible to all files. That contrasts with static.
Static and extern differ in visibility. It's also notable that neither of these approaches duplicates the string (not even #define) as the compiler uses String Interning to prevent that. In this NSHipster post they show proof:
NSString *a = #"Hello";
NSString *b = #"Hello";
BOOL wtf = (a == b); // YES
The operator == returns YES only if the two variables point at the same instance. And as you can see, it does.
The conclusion is: use FOUNDATION_EXPORT for global constants. It's debug friendly and will be visible allover your project.
After doing some search (this question/answer among other things) I think it is important to say that anytime when you are using string literal #"AppQuitGracefully" constant string is created, and no matter how many times you use it it will point to the same object.
So I think (and I apologize me if I'm wrong) that this sentence in above answer is wrong: If you use a #define, there will be a separate copy of the string stored in the source on each use.
I use static when I need to export NSString symbols from a library or a framework. I use #define when I need a string in many places that I can change easily. Anyway, the compiler and the linker will take care of optimizations.
USING #define :
you can't debug the value of identifier
work with #define and other macros is a job of Pre-Processor,
When you hit Build/Run first it will preprocess the source code, it will work with all the macros(starting with symbol #),
Suppose, you have created,
#define LanguageTypeEnglish #"en"
and used this at 2 places in your code.
NSString *language = LanguageTypeEnglish;
NSString *languageCode = LanguageTypeEnglish;
it will replace "LanguageTypeEnglish" with #"en", at all places.
So 2 copies of #"en" will be generated.
i.e
NSString *language = #"en";
NSString *languageCode = #"en";
Remember, till this process, compiler is not in picture.
After preprocessing all the macros, complier comes in picture, and it will get input code like this,
NSString *language = #"en";
NSString *languageCode = #"en";
and compile it.
USING static :
it respects scope and is type-safe.
you can debug the value of identifier
During compilation process if compiler found,
static NSString *LanguageTypeRussian = #"ru";
then it will check if the variable with the same name stored previously,
if yes, it will only pass the pointer of that variable,
if no, it will create that variable and pass it's pointer, next time onwards it will only pass the pointer of the same.
So using static, only one copy of variable is generated within the scope.

Objective-C #define directive demonized for String constants

I've reading in several post and in Apple's code guidelines that in Objective-C String constants should be defined as extern NSString *const MY_CONSTANT; and that the #define directive should be avoided. Why is that? I know that #define is run at precompile time but all string will share the same memory address. The only advantage I read was that if the constant must be updated or changed you don't have to recompile your entire project. So that i s the reason why #define should be avoided?
Thanks
UPDATE: In this case is good to use a #define or there is a better approach?
/* Constants Definition */
#define SERVER_URL #"http://subdomain.domain.edu.ar/Folder/"
NSString *const ServerURL = SERVER_URL;
NSString *const LoginURL = SERVER_URL#"welcome.asp";
NSString *const CommandURL = SERVER_URL#"com.asp";
A practical reason to use the constant as opposed to the definition is that you can do direct comparisons (using ==) instead of using isEqual:. Consider:
NSString * const kSomeStringConstant = #"LongStringConstantIsLong";
...
[someArray addObject:kSomeStringConstant];
if ([someArray lastObject] == kSomeStringConstant)
{
...
}
This would work, since the == comparison would be comparing identical const pointers to a single NSString object. Using #define, however:
#define STRING_CONSTANT #"MacrosCanBeEvil";
...
[SomeArray addObject:STRING_CONSTANT]; // a new const `NSString` is created
if ([someArray lastObject] == STRING_CONSTANT) // and another one, here.
{
...
}
This would not work out, since the two strings would have unique pointers. To compare them effectively, you would have to do a character-by-character comparison using isEqual:
if ([[someArray lastObject] isEqual:STRING_CONSTANT])
{
...
}
This can be far more costly in terms of execution time than the simple == comparison.
Another motivation could be the size of the executable itself. The #defined constant would actually appear, in place, wherever it was used in the code. This could mean that the string appears many times in your executable. In contrast, the constant should (with modern compilers) be defined only once, and all further usages would make reference to the pointer to that one definition.
Now, before anyone yells at me for premature optimization, consider that the two approaches are almost identical in terms of implementation, but the const pointer method is far superior in terms of code size and execution time.
It's not necessarily guaranteed that there will only be one NXConstantString object for a given string literal in an entire application. It seems pretty likely that different compilation units might have different objects for the same constant string. For example, if somebody writes a plugin, one constant string will be generated for occurrences of that NSString literal in the plugin and one will be generated for occurrences in the host application, and these will not be pointer-equal.
The best argument I have heard is that const strings show up in the debugger, whereas macros do not.
static NSString * const SERVER_URL = #"http://subdomain.domain.edu.ar/Folder/";
As far as I know, #define only lets you define C-style string constants. To create a constant NSString object, you have to declare it in the header, and then give it a value in one of your .m files.
Header file:
extern NSString *MyConstantString;
Main file:
NSString *MyConstantString = #"String value";

problem with declare a global variable in objective c

i have a global variable in 1 of the class like
classA.h
const NSString *global;
classA.m
global=[array objectAtIndex:0];//a array store sort of string
and in another class i want to call this global variable
classB.m
import "class.h"
NSLog(#"%#",global);
but it doesnt work,i know when i jus assigned a value directly to my global variable instead of from another variable it will work but can somebody show me how to make it achieve from a array?
In the header, use:
extern const NSString *global;
and in the implementation (.m):
const NSString *global;
The "extern" reference tells all including files that the variable exists, but it's declared elsewhere. Finally, in your implementation file, you put the actual declaration.
You cannot do this like that.
const NSString *global;
NSString const *global;
both mean a pointer (that can be changed) to a constant NSString object. In Objective-C constant objects make no sense. The compiler cannot enforce the constness of objects. It can not know wether a method changes the internal state of an object or not. Also all the classes in the library always take pointers to non-constant objects as parameters for their methods, so having any const object pointers will cause a lot of warnings.
On the other hand there are constant pointers to objects which are declared like this:
NSString * const global;
This means the pointer points to a regular NSString object, but it’s value cannot be changed. This means that you also have to initialize the value of the pointer (it cannot be changed later). This is used to define constants. But this only works with NSStrings and string literals. For all other classes there is no way to specify a compile-time constant object thats required for the initialization. And in this case it is a true constant - string literals are immutable by definition.
But in your case you can do away with the const. You want to change the pointer later so it cannot be a NSString * const. If you insist on a global you’d just have to make it a regular NSString *. On the other hand - globals are evil. You should change your design so you don’t need it.