I have a huge Objective-C project which I want to split into several ones in a single WorkSpace. Initially I did it, and code compiled well, but I decided also to move one category of a big class to other project, and now linker doesn't understand the situation:
Undefined symbols for architecture x86_64:
"_OBJC_IVAR_$_MyClass.data", referenced from:
-[MyClass(Viz) someTask] in MyClass+Viz.o
d: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
Briefly, my workspace looks like this:
- MyLib project:
MyClass.h
MyClass.m
MyClass+Inner.h
MyClass+Inner.m
- MyApp project:
MyClass+Viz.h
MyClass+Viz.m
MyLib is compiled into MyLib.framework which is used in MyApp. As I said, before moving MyClass+Viz to MyApp project, all the complex logic worked correctly, so there is probably no problem with projects linking. Also, I checked twice that all the files are marked correctly in Build Phases settings section.
This is the MyClass.h file:
#interface MyClass : NSObject
- (instancetype)init;
- (void)public_methods;
#end
This is the MyClass+Inner.h file:
#import "MyClass.h"
#interface MyClass ()
{
// Variables placed here
// so that only class methods can access them
SomeStruct* data;
// other fields ...
}
#end
#interface MyClass (Inner)
- (void)private_methods;
#end
And the MyClass+Viz.m file:
#import "MyClass+Viz.h"
#import "MyClass+Inner.h"
#implementation MyClass (Viz)
- (int)someTask {
return data->length;
// this or any other stuff with
// class variables from MyClass+Inner.h
// leads to "Undefined symbols" by linker
}
#end
How can I make it work, make linker see class' private variables from other project?
First, you should be using #property and not instance variables, generally.
Secondly, you would do something like this using multiple header files and class extensions.
Typcially,
MyClass.h
MyClass_Internal.h
MyClass.m
MyClass_Internal.m (Not typically used)
MyClass.m would import both headers. MyClass.h would define the public interface and MyClass_Internal.h would have a class extension to provide the private interface:
#interface MyClass()
#property(...) Thing *internalThing;
#end
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.
I'm just a newbie who follow iOS dev, and when I create new single application by using Xcode, I notice that #interface AppDelegate() being declared again in AppDelegate.m file.
but to be honest, I really don't understand why, I already see #import "AppDelegate.h" at begin of AppDelegate.m file, why couldn't I directly use #implementation AppDelegate after #import "AppDelegate.m"?
There is a difference here. The declaration of the AppDelegate Interface in the .m file is actually a class extension. Notice the () after the interface declaration.
As mentioned in the documentation:
Class extensions are often used to extend the public interface with
additional private methods or properties for use within the
implementation of the class itself.
I want to remove warnings from an older project that somebody else has written.
I am confronted with over 200+ warnings about an instance method in a category overrides a method from class.
So for example:
In header:
#interface Foo : NSObject
//...unimportant code
#end
#interface Foo()
#property(nonatomic, retain)NSArray *bar;
#end
#interface Foo (Private)
//...unimportant code
#end
In body:
#implementation Foo
//...unimportant code
#end
#implementation Foo (Private)
- (NSArray*)bar
{
//...
}
#end
Generated linker waring:
ld: warning: instance method 'bar' in category from Foo.o overrides method from class in Foo.o
So obviously the getter for the bar property is implemented in the wrong category.
I can solve the problem if I copy the method in the category above.
But do I have to do this manually in over 200 classes?
Or does a refactoring tool for this problem exist or does a flag for the linker exist to suppress exactly this type of warning?
Anyone know of an example project of a Objective-C project calling Objective-C++ code.
I've read all the stackoverflow q's about getting one to call the other but no luck.
Would help if I had a code sample that worked.
The Obj-C++ I have is a lib .a and some c++ headers. Thats all.
Here's example project I made, it's actually easy to undestand, you can just rename your classes from .m to .mm or in project settings set "Compile sources as" to Objective-C++. I have had some trouble with C++ headers which didn't have C++ implementation as well - changing settings to compile everything as Objective-C++ helped.
Here's the code https://github.com/libec/StackOverflow/tree/master/03-Obj-C%2B%2B
Here's a contrived example. Without more information about what you're trying to do, or the errors you're receiving, it will be impossible to guide you much further. Remember that any code that calls C++/Objective-C++ code or imports a header that includes C++-isms must be compiled as Objective-C++ (use the .mm extension and Xcode will automatically do the right thing).
/*Objcpp.h
**********/
#interface MyClass : NSObject
{}
- (void)myMethod;
#end
/*Objcpp.mm
***********/
#import "Objcpp.h"
#implementation MyClass
- (void)myMethod {
//some c++ and/or objective-c calls
}
#end
/*myobjc.h
***********/
#interface MyObjCClass : NSObject
{}
- (void)someMethod;
#end
/*myobjc.mm
***********/
#import "myObjCClass.h"
#import "Objcpp.h"
#implementation MyObjCClass
- (void)someMethod {
MyClass *o = [[MyClass alloc] init];
[o myMethod];
}
#end
I am trying to build the Clustering Plug in my project under Leopard. I have following two questions.
In the project an interface class is defined as
#interface ClusteringController : NSWindowController
{
.......
.....
....
}
#end.
And this class is used in implementation class using forward declaration:
#class ClusteringController;
then in one function it is used as:
- (long) filterImage:(NSString*) menuName
{
ClusteringController *cluster = [[ClusteringController alloc] init];
[cluster showWindow:self];
return 0;
}
When I build this project it produces the warning:
warning: receiver 'ClusteringController' is a forward class and corresponding #interface may not exist
Also there is one more warning produced:
warning: no '-updateProxyWhenReconnect' method found
This warning is coming for the following line of code:
if(delegate) [delegate updateProxyWhenReconnect];
Can anybody help me to overcome these warnings?
A forward declaration is used when the header file will be imported after the interface. It looks to me that you've used the #class directive after the interface for the class itself.
The normal use of a forward class declaration looks like this:
#import "SomeSuperClass.h"
#class Forwardclass;
#interface SomeClass : SomeSuperClass
{
Forwardclass anIvar;
}
#property Forwardclass anIvar;
#end
#import "SomeClass.h"
#import "ForwardClass.h"
#implementation SomeClass
#synthesize anIvar;
-(void) setAnIvar:(ForwardClass *) aForwardClass;
#end
The #class directive is never used in an implementation (.m) file.
That's not what #class is for.
You use #class in the header file for another class, to tell the compiler that the class you're declaring does exist. Without it, the compiler would not know that that's a class name, and when you declare a variable as holding a pointer to an instance of that class, the compiler would think that you're just making up words. Using #class is called forward-declaring the class.
Your situation is different. You're in the implementation file for that class.
What the compiler needs from you now is the class's #interface. The warning is telling you that the compiler needs an #interface, but you haven't given it one (so, as far it knows, the #interface “may not exist”).
Normally, you would have written the #interface in a header file; how now to get it into the implementation file?
That's where the preprocessor comes in, with its #import directive. At the top of the implementation file (ClusteringController.m), import the header file:
#import "ClusteringController.h"
The preprocessor will replace this with the contents of that file, then hand the preprocessed code to the compiler, which will see the #interface there.