Static const definitions in .h vs external const in .m - ios

I've find it frustrating to define constants in the .h as externals and then assign the constants in the .m file. Seems so redundant. Is there any reason not to just define the constants in the header file?
Typical implementation would be:
// Constants.h
#interface Constants : NSObject
extern NSString *const kPCFavorites;
#end
The implementation would then be:
// Constants.m
#implementation Constants
NSString *const kPCFavorites = #"PCFavorites";
#end
However, I can just do this:
// Constants.h
static NSString *const kPCFavorites = #"PCFavorites";
#interface Constants : NSObject
#end
Obviously, this last definition doesn't even need an interface or implementation so both could be left out and become:
// Constants.h
static NSString *const kPCFavorites = #"PCFavorites";
with no .m file at all.
This seems much cleaner to me. Why wouldn't we implement constants this way? I've defined them both ways and I get no compile or runtime errors in XCode 5.

Because
static NSString * const kPCFavorites = #"PCFavorites";
declares a variable, not a constant. C doesn’t actually have a way to declare a symbolic constant (besides enum, which only works for integers).
As a result, if you use this method, every file that #includes your header will have its own variable called kPCFavorites. Historically, that would have meant that your program would increase in size because of all of the copies of kPCFavourites and the string #"PCFavourites", although more modern linkers may manage to get rid of some or all of the duplication (certainly I’d expect the linker to leave you with only one copy of the string itself; whether or not it can currently get rid of the extra pointer variables I’m not sure — but it’s easy to test).

static variables have file scope. If you compile a file which includes a header file with a static variable, that variable will exist in the compiled file. If you compile another file with the same header file, you have a second static variable and so on. If you include the header from 1000 source files, you get 1000 static variables, all with the same name.

Related

in XCode objective-C game project, how to include a .swift file with constants delcared in it

I have a game in Objective-C and I have constant in it declared like this, in file GameplayConstants.h:
#ifndef GameplayConstants_h
#define GameplayConstants_h
static float REGULAR_TIME_PER_FRAME = 0.1f;
#endif /* GameplayConstants_h */
I am converting game to swift, so I want to convert one by one file to swift. In particular, I want this constant (REGULAR_TIME_PER_FRAME) converted to a swift file, to a constant variable in a swift file GameplayConstants.swift (rather than in the GameplayConstants.h file which it currently sits in now). What should I do? I set the defines module in configuration file to YES, and I included the file (with line include GameplayConstants.swift), but I have various errors. At the moment, error is:
"missing #end"
in HeroAnimationhelper.m file, which has code (partial code displayed):
#import "HeroAnimationHelper.h"
#import "HeroConstants.h"
#import "dealer-Swift.h"
#implementation HeroAnimationHelper
//#include "GameplayConstants.h"
#include "GameplayConstants.swift"
As you see, I'm replacing the original .h file with .swift file.
What else do I need to do to use swift constants in a objective-c class?

Linker error when referencing static NSString const in Swift [duplicate]

I created Objective C Header file. and added some properties in it.
i declared
static NSString* const kColor005C98 = #"005C98"; in Constants.h file
I defined this file in Bridging-Header file as #import "Constants.h"
Now when i want to use this property kColor005C98 in some swift file it failed the build and i am getting
Undefined symbols for architecture armv7: "_kColor005C98", referenced from:
i don't know what else i need to do so i don't get this error? (i have used this property in other objective C file successfully and no issue in that case)
Update:
As of Swift 2/Xcode 7 and later, a static constant definition like
static NSString* const kColor005C98 = #"005C98"; // in Constants.h file
is imported to Swift and can be used without problems.
(Old answer for Swift 1.x) When the code
static NSString* const kColor005C98 = #"005C98"; // in Constants.h file
is processed by an Objective-C compiler, it is treated as two things
combined into one statement:
A variable declaration which introduces an identifier and describes its type, and
a variable definition which actually instantiates/implements this identifier.
See for example
What is the difference between a definition and a declaration?
for a good explanation of the difference between declaration and
definition.
The Swift compiler treats the statement only as a declaration.
Therefore the variable is not defined anywhere, causing the linker error.
To solve the problem, you have to move the definition to an Objective-C
file:
// Constants.m:
#import "Constants.h"
NSString * const kColor005C98 = #"005C98";
and change the declaration to an extern declaration:
// Constants.h:
extern NSString * const kColor005C98;
Alternatively, you can just remove the static modifier:
NSString * const kColor005C98 = #"005C98";
to make it work with Swift. The disadvantage is that when
this line is included by multiple Objective-C files, all of them
will define a globally visible symbol kColor005C98, causing
"duplicate symbol" linker errors.
Another alternative is to use a macro definition instead:
#define kColor005C98 #"005C98"

How is static const different from extern const?

In my iOS/Objective C projects, I often have a constants.h file with my API keys and such. Until today, I've been declaring my constants as static const like so:
static NSString * const kAPIKey = #"wembvkejrvb43789gvbiu2bvfake";
This works fine, but with the unfortunate downside that I can only create constants for primitives and NSString literals, essentially. Other objects, such as UIColor objects, cannot be stored in this constant as they cannot be initialized with a static literal syntax (my understanding, citation required).
After reading some C++ docs, I understand a few things:
The static is unnecessary, as const are implicitly static.
Calling NSString * const x is actually declaring a constant and immutable value in x. I cannot change the value, but may be able to change what x points to.
This const has internal linkage, meaning the the value is defined right away (presumable at compile time).
Are these conclusions correct?
How does an extern const differ? I assume they are externally linked (thus the extern keyword). Are they defined at run time? Can I create some sort of dynamic extern const that can be set with a value returned by a class method?
For example, I would like to create a globally-scoped constant that contains a UIColor value. I would like to construct this color value using the [UIColor colorWithRed:green:blue:alpha:] class method. This clearly doesn't work with the internally linked constants I've been using (I'm assuming because it happens at compile-time) - but is it possible using an external constant, possibly set up in the +initialize method?
Any elaboration on the details of this behavior would be immensely helpful.
The static is unnecessary, as const are implicitly static.
No, that's not true.
static when used at file scope (i.e. outside any method or function) means that the variable is visible only within that file.
extern means that the variable is defined in some other file.
const means that the variable cannot be modified.
Consider strings. Often, you'll have an implementation file (name ends in .m) that defines some constant string pointer:
NSString *const SomeString = #"some string";
You might want to use that same constant from other files. If so, you could add a declaration to the header (name ends in .h) file that explains to the compiler that the variable is defined elsewhere:
extern NSString *const SomeString;
and that would let you use SomeString in any file that imports the header file. On the other hand, you might decide that you definitely do not want the constant used outside the implementation file. In that case, you could declare it static (in the implementation file again):
static NSString *const SomeString = #"some string";
and that would prevent its use from outside the file.
Calling NSString * const x is actually declaring a constant and immutable value in x. I cannot change the value, but may be able to change what x points to.
Right, it declares the pointer x to be constant -- you can't change it. You also can't change the value that it points to if it's actually a NSString because an instance NSString isn't mutable.
This const has internal linkage, meaning the the value is defined right away (presumable at compile time).
I'll take the 5th on that -- I'm not sure exactly how the compiler deals with constant strings. I think it's safe to use that as a mental model, though; the string will in any case be defined before your code ever gets to use it.
On your specific programming question, how to create a compile-time defined color object: you can't, because, apart from the handful for which the language supplies literal syntax, objects are created at runtime.
But you can still do it elegantly at runtime, without any addition to global scope, the same way the sdk does...
#interface UIColor (RainbowAddition)
+ (UIColor *)chartruseColor;
#end
#implementation UIColor (RainbowAddition)
+ (UIColor *)chartruseColor {
// bonus: this is really chartruse... according to the internet
return [self colorWithRed:0.5 green:1.0 blue:0.0 alpha:1.0];
}
#end
http://cloford.com/resources/colours/500col.htm
Objective-C is a pure extension of C, C++ is not.
At global scope:
in C (and Objective-C) writing const is equivalent to extern const (external linkage);
in C++ writing const is equivalent to static const (internal linkage).
Both in C (and Objective-C) and C++, to create a global-scoped const you can define it, just one time, in one source file like extern const TYPE identifier = VALUE; and declare it (typically in an header file) like: extern const TYPE identifier; (read it: I defined this const elsewhere at global linkage level).

Global variables in my Constants file

Until now if I needed access to a global variable across my app i just added
#define PATH [NSString stringWithFormat:#"www.url.com"]
To my Constants.h file.
I need to fetch the PATH value from my server.
How can i assign the value I'm getting from a server to a macro \string like the above and still be able to use just the variable PATH anywhere in my app? (Without naming the class like a property use such as class.PATH
This works:
#import <Foundation/Foundation.h>
NSString* PATH;
#interface Constants : NSObject
+(void)getPathFromServer;
#end
And PATH is accessible from anywhere in my app but I'm not sure if that should be the way to go.
As I understand so far, you need to define a macro which dynamic change it's url content.
If I'm right , you may need a Vararg Macros which takes a variable.
#define PATH(...) [NSString stringWithFormat:#"%#",__VA_ARGS__]
You can use extern keyword
Example :
//Header file
extern NSString * const path;
// .m file under implementation
NSString * const Ppath = [NSString stringWithFormat:#"www.url.com"];
Have a look at these
Constants in Objective C
#define vs const in Objective-C

Forward declare structs in Objective C

I'm writing an iOS app in which I have a model class that is going to initialize itself with an XMLElement I give to it.
I'm using TBXML for the XML part.
The header for the model class looks like this:
#interface CatalogItem : NSManagedObject
#property (nonatomic, retain) NSString * name;
#property (nonatomic, retain) NSManagedObject *group;
-(id)initWithXMLElement:(TBXMLElement*)element;
#end
Now instead of including the header in which TBXMLElement is defined, I'd like to forward declare it with: struct TBXMLElement before the class definition. I'm however getting an "Expected Type" error wich tells me my declaration isn't working. Is this not how I would got about this?
As I understand it, including header files in header files is bad practice. The compiler doesn't need to know the inner workings of TBXMLElement, just that it exists or will exist at compile time.
Forward declaration of structs are used all the time, but still involves importing a header. The motivation is to not allow developers to dip into the structure directly. I.e. look at CFString. It is implemented as a struct, but you can't touch the structure contents directly. Instead, there is a full API for manipulating the struct contents. This allows CFString's implementation details to change without breaking binary compatibility.
In your header (ideally the header that defines whatever API is associated with TBXMLElement*):
TBXMLElement.h:
typedef const struct TBLXMLElement *TBXMLElementRef;
extern TBXMLElementRef TBLXMLCreateElement();
... etc ...
Then, in the implementation file containing the implementation of the TBLXMLElementAPI:
TBXMElement.c (assuming it is a C file):
typedef struct __TBLXMLElement {
... struct members here ...
} TBLXMLElement;
TBXMLElementRef TBLXMLCreateElement()
{
return (TBXMLElementRef)malloc(sizeof(TBLXMLElement));
}
... etc ....

Resources