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.)
Related
converting my project to ARC but says it can't due to the following error 'No matching function for call to pthread_create'. Here is the code it falls in, happens specifically on the line starting with pthread create. How can I fix this? It also says Candidate function not viable: no known conversion from 'NSString *' to 'void * _Nullable' for 4th argument in the sidebar underneath the error.
I've cut off the rest of the function but can provide more detail if necessary.
void World::loadWorld(std::string name)
{
if(doneLoading==0)
{
doneLoading=1;
Resources::getResources->stopMenuTune();
if(LOW_MEM_DEVICE)
{
menu->deactivate();
Resources::getResources->unloadMenuTextures();
terrain->allocateMemory();
terrain->loadTerrain(name,TRUE);
doneLoading=2;
hud->fade_out=1;
}
else
{
terrain->allocateMemory();
pthread_t foo;
pthread_create(&foo,NULL,loadWorldThread, nsstring(name));
}
}
As your error message indicates the 4th argument to pthread_create is of type void *. Under ARC you cannot simply pass an Obj-C object reference as a void * as ARC would is not able to track the reference once it is stored in a C(++) pointer variable, and therefore cannot manage the object's memory.
For situations where an Obj-C reference must be passed into the C(++) world a bridge cast can be used to inform ARC how the memory should be managed. However in your case there a better way, just pass the C++ pointer, name, without creating an NSString. If loadWorldThread expects a std::string that is the correct thing to do anyway. If it expects an NSString * then either:
modify it to take a std::string and do any required conversion to NSString * within it; or
write a small intermediate function which takes a std::string, produces an NSString * from it, and then calls loadWorldThread. Pass this new function to pthread_create.
Doing either of the above avoids the use of a bridge cast in the pthread_create call to move the Obj-C reference into the C(++) world and out of ARC control; and another bridge cast in loadWorldThread (or intermediate function as above) to move it back into the Obj-C world and into ARC control.
Addendum
Expanding on the last paragraph, as the method there seems better suited to your situation. First, it is assumed that your code:
nsstring(name)
takes a value of type std::string and returns a value of type NSString, if it does not then look up how to do this conversion.
After the above expression you have a reference to an NSString under ARC control. You cannot simply pass such a reference as a void *, you must take it out of ARC's control first and take responsibility for its memory management (but not for long as you will see). You can bridge cast your NSString * to a CFStringRef:
CFStringRef cfName = (__bridge_retain CFStringRef)nsstring(name);
You can now pass cfName, which is a reference to a heap-allocated CFString, as a void *.
Now in loadWorldThread; which should be declared to take a void *, something like void loadWorldThread(void *arg) { ... }; you need to bridge cast your CFStringRef back to NSString * and hands responsibility for its memory management back to ARC:
NSString *nsName = (__bridge_transfer NSString *)arg;
The above is a standard pattern to pass an ARC controlled reference though an anonymous reference (void *).
(Note: the above uses CFStringRef to make it clear that you are passing around a reference to a manually managed CFString, you can cast directly to void * and back again, indeed you will notice that when casting back arg was not first cast to a CFStringRef to demonstrate this.)
HTH
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.
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.
When casting around (no pun intended) to clarify when to use __strong in a variable declaration I came across these lines in the Transitioning to ARC Release Notes:
You should decorate variables correctly. When using qualifiers in an object variable declaration, the correct format is:
ClassName * qualifier variableName;
for example:
MyClass * __weak myWeakReference;
MyClass * __unsafe_unretained myUnsafeReference;
Other variants are technically incorrect but are “forgiven” by the compiler. To understand the issue, see http://cdecl.org/.
I suspect this is some sort of in-joke on Apple’s part, but I don’t suppose I get it. It clearly doesn’t matter but I would like to do it right. What is the importance of correctly “decorating” a variable declaration, and what point is cdecl.org trying to make?
Edit: to clarify, I want to understand precisely why writing
qualifier ClassName * variableName;
is "technically incorrect."
So I think I have an answer, but I can’t be sure if I’m correct. Feel free to provide a better one, or comment/upvote if you think I’ve nailed it.
CDecl is a C program which you can download from that same website. It exists to solve problems such as in this question.
Variable declarations in C can be pretty notorious, especially when typedefs are taken into account. There is a good introduction to this over at Unixwiz.net. You’ll find there a helpful introduction that will allow you to read even such monsters as char *(*(**foo [][8])())[]; (foo is array of array of 8 pointer to pointer to function returning pointer to array of pointer to char).
The rule with these is effectively proximity. Consider the simple example
const int * foo
This declares foo as a pointer to a constant int. However,
int * const foo
will declare foo as a constant pointer to an int. The subtle difference is discussed thoroughly in this question. (essentially, in the first example you can make foo point to another constant int, but cannot modify the constant int through foo; in the second, you can modify the int which foo points to, but you can’t make foo point to any other location in memory).
With these ARC attributes, the syntax of using
__strong NSString * myString
would declare myView as a pointer to a “strong” UIView. This is nonsensical. Only pointers to objects can have the attribute of being strong (or weak or whatever) under ARC. Therefore it is good practice to write
NSString * __strong myString
since this is in line with other C keywords.
I did ask myself: what happens under ARC if you declare a strong object pointer to a weak object pointer to, say, an NSString, like so
NSString * __weak * __strong myContrivedPointer;
but the same applies. Nothing but an object pointer can have these keywords. Consequently, it is nonsensical to declare a pointer to a pointer “strong”.
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.