I read through the advice here Constants in Objective-C, but I find two errors in the accepted answer:
I keep getting linker errors when I implement the approach of using .m and .h
In my constants.m file, I get the error "unknown type" for NSString
I am also getting an error following the advice from #VictorHanHee
as a further suggestion of how to link the constants to a .pch file:
I don't have a .pch file by default, and when I create a new one it
doesn't have the formatting expected from the answer.
All I have done is create constants.h and constants.m and put the constant declarations in them as provided in the accepted answer. I also created a .pch file, but as I said it doesn't at all match VictorHanHee's description.
Is this older posting outdated, or if not what am I missing? I don't really know how to go forward with a linker error. I simply want to have a file constants.h where I declare all constants and can include this file in as many classes as I want to make life easier. Can someone tell me how to do this nowadays or point to a more recent discussion?
Also here is the linker error (or part of log I can copy and paste anyway, not full message)
-[AccountInfoViewController viewDidLoad] in AccountInfoViewController.o
ld: symbol(s) not found for architecture armv7
clang: error: linker command failed with exit code 1 (use -v to see invocation)
As you can see, I am trying to access the constant from viewDidLoad. I am simply trying to print it out with NSLog.
Here are the .h and .m files I was originally using:
.h
extern NSString * const PREFS_MY_CONSTANT;
.m
NSString * const PREFS_MY_CONSTANT = #"prefs_my_constant";
Ok here is the template that actually seems to work:
.h
#ifndef Project_prefs_h
#define Project_prefs_h
#endif
extern NSString * const PREFS_MY_CONSTANT;
.m
#import <Foundation/Foundation.h>
NSString * const PREFS_MY_CONSTANT = #"prefs_my_constant";
So the older post is perhaps misleading or I misunderstood them when they said remove everything apart from the constants - but if you don't actually want to remove everything, shouldn't you say so? That's a fair amount of text in there not to mention...
In recent version of Xcode there is no ProjectName-Prefix.pch created automatically file so you should import the file where it is required then try using the constant.
Easiest way:
// Prefs.h
#import <Foundation/Foundation.h>
#define PREFS_MY_CONSTANT #"prefs_my_constant"
Better way:
// Prefs.h
extern NSString * const PREFS_MY_CONSTANT;
// Prefs.m
NSString * const PREFS_MY_CONSTANT = #"prefs_my_constant";
There are a few different ways to create constants in Objective-C, but the easiest way is to use #define statements.
To start, create a new header file. This is where we’ll store all the constants. Name it something original like “Constants.h”.
Here’s a sample file with a few constants:
//
// Constants.h
// App Name
#import <Foundation/Foundation.h>
#define NUM_SECTIONS 7
#define NUM_SECTION_1_ITEMS 2
#define NUM_SECTION_2_ITEMS 14
#define NUM_SECTION_3_ITEMS 5
#define APP_TITLE #"App Name"
#define APP_AUTHOR #"Miscellanea"
To use these constants in your project, you need to import your header file in each implementation file (*.m) where you’ll be referencing them.
#import "Constants.h"
– (void)viewDidLoad
{
self.title = APP_TITLE;
for (int i = 0; i < NUM_SECTIONS; ++ i)
{
// etc.
}
}
Here is the template that does seem to work. It's not different from the old answer except that you don't delete everything apart from the constants.
.h
#ifndef Project_prefs_h
#define Project_prefs_h
#endif
extern NSString * const PREFS_MY_CONSTANT;
.m
#import <Foundation/Foundation.h>
NSString * const PREFS_MY_CONSTANT = #"prefs_my_constant";
Apart from this it appears no longer possible to use .pch file to automatically include the constants across all classes.
Related
I am aware this is not standard or conventional, please read on. I have a header file that defines the interface and implementation of an Objective-C class.
Person.h
#ifndef Person_h
#define Person_h
#interface Person : NSObject
-(void)speak;
#end
#implementation Person
-(void)speak
{
// Say something
}
#end
#endif /* Person_h */
I also have two source files that both include the header file.
Main.mm
#import Foundation;
#import "Person.h"
int main(int argc, const char * argv[])
{
// Do nothing
}
Test.mm
#import Foundation;
#import "Person.h"
When the project is built, I get duplicate symbol errors.
duplicate symbol '_OBJC_CLASS_$_Person' in:
/Debug/TestBox.build/Objects-normal/x86_64/main.o
/Debug/TestBox.build/Objects-normal/x86_64/test.o
duplicate symbol '_OBJC_METACLASS_$_Person' in:
/Debug/TestBox.build/Objects-normal/x86_64/main.o
/Debug/TestBox.build/Objects-normal/x86_64/test.o
ld: 2 duplicate symbols for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
I want to be able to include the same header file in multiple source files. In C++, I can inline the implementation into the header file like this:
#ifndef Person_h
#define Person_h
class Person
{
public:
void speak()
{
// Say something
}
};
#endif /* Person_h */
However, I haven't been able to find a way to do that with Objective-C. I'm using Objective-C so I can subclass events from NSWindowDelegate and NSResponder.
I tried searching for solutions. Most of them said to separate the implementation into a source file, but that would break the single-header architecture. One suggestion is to use the Objective-C runtime library and create my classes at runtime. This appears to give me the results I'm looking for, but I'm wondering if there is a simpler way.
Is there some way to implement an Objective-C class in a header file so it can be included by multiple source files? Or is dynamically creating the classes at runtime my best option?
Update
I am looking for a solution that avoids using a .m or .mm file to write the Objective-C implementation. Even if doing so is not the conventional way to do it. The single-header file is required by the architecture of the project I am working on. The project is cross-platform, and the single-header design isn't an issue in C++ on Windows and Linux. Adding a source file to define Objective-C objects would break the existing architecture.
Thats not purpose of header file. In header file you will define properties and methods which you want to make available where needed.
Implementation always lies within .m file in case of Objective C.
EDIT
Sure thing, of course you can ... I've removed the previous failed attempt to remove clutter.
// main.m
#import <Foundation/Foundation.h>
#import "A.h"
#import "B.h"
#import "C.h"
int main(int argc, const char * argv[])
{
#autoreleasepool
{
NSLog(#"Hello, World!");
[[A alloc] init];
[[B alloc] init];
[[C alloc] init];
}
return 0;
}
// A.h, repeat for B.h and C.h
#import <Foundation/Foundation.h>
#interface A : NSObject
#end
// A.m
// B.m and C.m are quite similar
// BUT drop the line below from B and C
#define ZIMP
#import "A.h"
#import "Header.h"
#implementation A
- ( id ) init
{
self = super.init;
[[[Z alloc] init] msg:#"A"];
return self;
}
#end
Now for the grand finale
// Header.h
#import <Foundation/Foundation.h>
#interface Z : NSObject
- ( void ) msg:( NSString * ) src;
#end
#ifdef ZIMP
#implementation Z
- ( void ) msg:( NSString * ) src
{
NSLog( #"%# says hi", src );
}
#end
#endif
The proof of the pudding ... here is the output
2020-07-15 07:29:52.134413+0200 HdrImp[26901:700649] Hello, World!
2020-07-15 07:29:52.135121+0200 HdrImp[26901:700649] A says hi
2020-07-15 07:29:52.135297+0200 HdrImp[26901:700649] B says hi
2020-07-15 07:29:52.135389+0200 HdrImp[26901:700649] C says hi
Program ended with exit code: 0
The trick is of course to get the compiler to see the implementation only once, even though the header file is included several times. This is accomplished with nothing but a single #define.
Now we can tell the Swift guys that Objective-C can be done with just a single source file.
UPDATE
This works because I know precisely which files will be included and I can set the guard in one of them. If you do not know beforehand which of the files will be part of the compile you have a problem but you can solve that with more defines and by some code in your make file where you 'know' which of the files will be included and can take action based on that.
You need to understand the different purposes of header (.h) and module (.m) files:
The correct usage is this:
A Header file just declares the types and what they are composed of. It is like saying to the compiler: Look, somewhere is a class named Person, and you can call speak on it. It does not tell the compiler the internals of the speak function, it just announces that this type exists somewhere.
This is called a type declaration.
When the compiler translates the Main.mm file, it creates a call to a yet-unknown, external function speak. It does the same for Test.mm.
Then, after compilation of your code, the linker runs to resolve all the unknown addresses, e.g. it checks if there is one and exactly one definition of a class Personwith a function speak. If there is no such definition, you get an unresolved symbol error. If there are multiple definitions, you get your duplicate symbol error.
Therefore, you need to create a Module file to define all your classes - exactly once. In your case you need a file Person.m, which contains the #implementation of the class (and functions).
Types can be declared multiple times, but may only be defined once.
What you did wrong: When you put your type definition into the header file, the compiler creates two implementations of your class (when translating Main.mm and the other when translating Test.mm), which then confuses the linker, because the linker expects exactly one.
You can have a central header file which aggregates all types and sub-includes, but you need one (or more) separate module files - and you must not include them, because the linker resolves the references for you.
If remove swift file from project it is complied successfully. if add then get error.
#defines get converted to Swift by trying to figure out the type. For instance
#define B #"b"
...becomes:
var B: String { get }
Find details in this answer.
As your define #define MY_EXPORT does not provide a type, the conversion fails.
This code would work:
Prefix.pch:
#define MY_EXPORT #"Olala"
MyOneClass.m:
#import "MyOneClass.h"
#implementation MyOneClass
- (void)method {
NSString *useIt = MY_EXPORT;
}
#end
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"
This is ridiculous, im trying to create a sound bool to turn of in app sounds. I keep getting
Undefined symbols for architecture i386:
"_kPlaySoundPrefsKey", referenced from:
-[AppDelegate application:didFinishLaunchingWithOptions:] in AppDelegate.o
ld: symbol(s) not found for architecture i386
clang: error: linker command failed with exit code 1 (use -v to see invocation)
I have checked that all my files are linked in build phases, I have deleted the appdelegate .m where im getting the error before I even get to call the bool in any of my view controllers, and re imported it in build phases. Checked I have relevant framweworks in place. I have even checked a previous app I made with same code and the code it appears to be exactly the same with no error (built with previous version of xcode). Taking it right back to basics I get the error as soon as I add the following code to my App Delegate,
.h
#import <UIKit/UIKit.h>
extern NSString *kPlaySoundPrefsKey;
#interface AppDelegate : UIResponder <UIApplicationDelegate>
#property (strong, nonatomic) UIWindow *window;
#end
.m
#import "AppDelegate.h"
#import <AudioToolbox/AudioToolbox.h>
#implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions: (NSDictionary *)launchOptions
{
NSDictionary *defaultDict = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES]
forKey:kPlaySoundPrefsKey];
return YES;
}
If I change extern NSString *kPlaySoundPrefsKey; to NSString *kPlaySoundPrefsKey; it builds then crashes...Im out of ideas now
When you declaring something as extern you are telling the compiler the type AND that you will define it somewhere else. In your case you never define your variable.
So in your .h you would have:
extern NSString* kPlaySoundPrefsKey;
But in some .m you must also have
NSString* kPlaySoundPrefsKey = #"play_sounds"; // or some value
Since in your case these are constants you can also specify:
extern NSString* const kPlaySoundPrefsKey;
and
NSString* const kPlaySoundPrefsKey = #"play_sounds";
The addition of the const qualifier will cause a compiler error if someone ever writes something like
kPlaySoundPrefsKey = #"some_other_value";
I am also got the same error even if I am properly declared and define the extern const string ,
The problem is, my constant file not in the compile source list .
Make sure that your .m file appears in the "Compile Sources" Build Phase for your build target. Sometimes, adding files to a project in Xcode doesn't add all implementation files to the appropriate targets.
Hope this will helpful for some people.
Refe: linker-error-undefined-symbols-symbols-not-found
First, ensure it is defined:
// AppDelegate.h
extern NSString* const kPlaySoundPrefsKey; // << declaration
// AppDelegate.m
NSString * const kPlaySoundPrefsKey = #"kPlaySoundPrefsKey"; // << definition
see also:
"extern const" vs "extern" only
3 questions about extern used in an Objective-C project
Linker error using extern "C" in Objective-C code
Thanks for your help guys
I added
NSString *kPlaySoundPrefsKey = #"playSoundKey";
[[NSUserDefaults standardUserDefaults] registerDefaults:defaultDict];
to app delegate. That fixed it
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 :)