Use static string from objective c in swift - objective-c

If in objective c I have a string declared in a header file on it's own, for example
static NSString* my_static_string = #"Hello world";
I am able to use it in swift and Xcode recognises it, however when I try to build I get "undefined symbol _my_static_string ..."
Is there any way I can use this string in swift?

The static keyword, when used with a global, limits the scope of the variable to the current .m file. It's generally used when defining a constant used within a particular .m file, but not to be referenced elsewhere.
It doesn't quite make sense to do that if you're defining a constant in a .h file. In that case, you usually define the variable in one of the .m files:
NSString * const kMyConstant = #"Hello world";
and then in the .h file you define the external reference to this object:
extern NSString * const kMyConstant;
If you use the above pattern, if the .h file is included in the bridging header, you should then be able to use the kMyConstant constant in your Swift code.

Related

Objective C reflection for C-type vars

I have few extern variables declared in #interface block of my .h file. They are assigned in .m file. In one method of my #interface (preferable class method) I want to enumerate over these variables via reflection, but I have no any idea is it possible at all, and how is yes ?
Example of my code:
Consts.h
extern NSString *MyConst1;
extern NSString *MyConst2;
Consts.m
NSString *MyConst1 = nil;
NSString *MyConst2 = nil;
+ (void)load {
MyConst1 = ...;
MyConst2 = ...;
}
+ (void)someMethod {
// i could use class_copyIvarList for objective c properties/ivars,
// but is it possible to get that MyConts1 and MyConst2 via reflection ?
// I understand, that C have different namespace,
// and I have no experience with it.
}
Objective-C supports reflection for Objective-C objects. The reflection works because every Obj-C class contains the necessary metadata (property names, method names etc.).
Objective-C doesn't in any way change the C language. Objective-C runtime is basically implemented in C and the compiler only translates Objective-C into C function calls.
No, you cannot use reflection for global variables outside the context of any Objective-C class.
If you want reflection, wrap them into a class.
However, note, that using reflection is almost never a good solution. Usually it is abused to solve a problem that is caused by bad architecture.
There are no class variables in ObjC. Only instances can have variables in ObjC. Even if you declare a global between #interface or #implementation and #end, it will still just be a global, ObjC doesn't actually know about it.
Only the ObjC parts in a .m file support introspection. One alternative approach would be to create a singleton that has methods returning your constants, then you can enumerate its methods. Alternately, you could create a function/method that returns an array of your constants. To avoid duplication of your constants and array, you could use the X Macro technique:
#define OSC_TYPE_CONSTANTS X(MyConst1) \
X(MyConst2)
// Header:
#define X(n) extern NSString* n;
OSC_TYPE_CONSTANTS
#undef X
// Implementation:
#define OSC_STRINGIFY(n) #n
#define X(n) NSString* n = OSC_STRINGIFY(n);
OSC_TYPE_CONSTANTS
#undef X
#define X(n) n,
NSArray* gAllTypesArray = [NSArray arrayWithObjects: OSC_TYPE_CONSTANTS nil];
#undef X
What do you need this for? Are these constants compiled directly into your app, or is this in a framework from which they are exported? Depending on that, there may be solutions involving CFBundle's CFBundleGetDataPointerFromName or dlsym.

Not allowing to use Constant defined in Objective C header file in swift class. Undefined symbols for architecture armv7

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"

Objective-C static, extern, public variables

I want to have a variable that I can access anywhere by importing a header file but I also want it to be static in the sense that there is only one of them created. In my .m file, I specify
static BOOL LogStuff = NO;
and in the initialize method I set the logging value:
+ (void)initialize
{
LogStuff = ... //whatever
}
However I want to be able to access my variable anywhere by importing the .h file so I want to do something like this:
static extern BOOL LogStuff;
but I'm not allowed to do that. Is it possible to do the thing I'm trying to do? Thanks
static in Objective-C means a different thing than static in a C++ class, in the context of static class data members and static class methods. In C and Objective-C, a static variable or function at global scope means that that symbol has internal linkage.
Internal linkage means that that symbol is local to the current translation unit, which is the current source file (.c or .m) being compiled and all of the header files that it recursively includes. That symbol cannot be referenced from a different translation unit, and you can have other symbols with internal linkage in other translation units with the same name.
So, if you have a header file declaring a variable as static, each source file that includes that header gets a separate global variable—all references to that variable within one source file will refer to the same variable, but references in different source files will refer to different variables.
If you want to have a single global variable, you can't have it in class scope like in C++. One option is to create a global variable with external linkage: declare the variable with the extern keyword in a header file, and then in one source file, define it at global scope without the extern keyword. Internal linkage and external linkage are mutually exclusive—you cannot have a variable declared as both extern and static.
An alternative, as Panos suggested, would be to use a class method instead of a variable. This keeps the functionality within the scope of the class, which makes more sense semantically, and you can also make it #private if you so desire. It does add a marginal performance penalty, but that's highly unlikely to be the bottleneck in your application (if you suspect it is, always profile first).
If LogStuff is a static class field, maybe you can implement static getter and setter?
+ (void)setLogStuff:(BOOL)aLogStuff;
+ (BOOL)logStuff;
Declare it extern BOOL in your header file. Files that #import your header don't know or care if the external symbol is static or not.
Separate global variable (one per source file):
// .h
static NSString * aStatic;
//.m
static NSString * aStatic = #"separate";
Unique global variable:
// .h
extern NSString * anExtern;
// .m
NSString * anExtern = #"global";
I normally use this layout for my statics:
NSMutableArray *macroArray;
BOOL keepMacro;
+ (void) startMacro
{
if (macroArray == nil)
{
macroArray = [[NSMutableArray alloc] initWithCapacity:100];
}
[macroArray removeAllObjects];
keepMacro = YES;
}
This is the startMacro command in my application. Both the Bool and the macroArray are static, but notice they are not declared static or extern.
This may not be the best practice, but this is what I do.

How to make NSString macro?

How to make a macro that represents a constant NSString value? I'm getting
"Multi-character character constant" and "Character constant too long for its type" warnings when defining in Xcode 4:
#define LEVELTYPEGLASS #"Glass"
Do I need to escape something?
Avoid using defines for string constants. Define as an extern in the header file like this:
extern NSString * const MYLevelTypeGlass;
And them implement in any implementation file:
NSString * const MYLevelTypeGlass = #"Glass";
This gives a few more characters to type, but adds allot of benefits like better typing for Xcode, guaranteed object identity (no duplicate strings). This is how Apple do it, if its good enough for them, it should be good for you.
The solution suggested by PeyloW is great. But I just want to note that I got the solution working after I have added #import "Foundation/Foundation.h" to header file. So the header file Constants.h should look like:
#import "Foundation/Foundation.h"
extern NSString * const LEVELTYPEGLASS;
#define IMAGECOUNT 5
...
Then, implementation file looks like:
#import "Constants.h"
NSString * const LEVELTYPEGLASS = #"Glass";
And if you need to include that into whole project you need to import that in -Prefix.pch file:
#import "Constants.h"
In that case, all the macro definitions resides in Constants.h header file, and some NSString constants resides in Constants.m implementation file. Again, thanks to PeyloW :)

Static Global variable in Obj-C?

// in ClassA.h
static NSString *globalStr = #"HelloWorld";
#interface ClassA
...
#end
// in ClassB.h
#include "ClassA.h"
// in ClassB.m
...
NSLog(#"The global string: %#", globalStr);
...
In C++, "static" should mean the variable or function has a internal linkage.
But it is used to share the variable in this case, error will occur without the static keyword.
I'm confused, could someone tell me the concept behind?
Thanks!
static means exactly the same thing in Objective-C that in means in C - it has internal linkage and static storage duration. You get an error without the static in this case because you will have a duplicate symbol globalStr in each object whose source code included ClassA.h. You're not sharing anything - you're getting a new copy of globalStr for each compilation unit.
Don't put object definitions in your headers and you'll be better off. If you want a single global string, you need to put
extern NSString *globalStr;
In ClassA.h, and define it in exactly one implementation file as:
NSString *globalStr = #"HelloWorld";