How can I conditionally declare a delegate in an interface declaration? - objective-c

I have an Xcode 4 project that builds to two different targets. I've defined some constants in the build settings so I can run different code for each target like this:
#ifdef VERSION1
// do this
#else
// do that
#endif
In one version of the app, I need the main view controller to open another view controller and become its delegate, but the other version doesn't use that view controller and shouldn't compile its code or try to become its delegate. I've set up the main view controller header like this:
#ifdef VERSION2
#import "SpecialViewController.h"
#endif
#interface MainViewController : UIViewController <MPMediaPickerControllerDelegate, SpecialViewControllerDelegate> {
// etc.
The conditional around the #import tag works fine, but how can I declare this class to be the SpecialViewControllerDelegate in one version but not the other?

Just use a #define preprocessor directive to change the delegates between versions.
Here's an example for "VERSION2".
#ifdef VERSION2
#import "SpecialViewController.h"
#define ARGS PMediaPickerControllerDelegate, SpecialViewControllerDelegate
#endif
#interface MainViewController : UIViewController <ARGS>

As long as you don't assign the delegate you should be fine leaving the implementation. Your SpecialViewController in VERSION1 (if you even have a SpecialViewController in V1) will not have a delegate so its calls will go nowhere, which should lead to no side effects.
#ifdef VERSION2
specialViewController.delegate = self;
#endif
If this approach doesn't work it almost seems like you should have a different MainViewController for each target.

Related

Can an Objective-C implementation be defined in a header file and also be imported by multiple source files?

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.

Why is a specific Swift class not visible to Objective-C?

All
Xcode 10.3 and Swift 5.0
I have a complex project with 3 targets ("Name", "Name Test" and "Name Local")
I have set all interoperability headers between Swift and Objective-C, including the Name-Bridging-Header.h, the Objc Preprocessor Macros to import the Name-Swift.h, Name_Test-Swift.h or Name_Local-Swift.h selectively
#ifdef TEST
#import "Name_Test-Swift.h"
#elif LOCAL
#import "Name_Local-Swift.h"
#else
#import "Name-Swift.h"
#endif
I have imported several Swift classes that I use frequently and everything works fine.
Now....
I added a new Swift class. This new class is subclass of NSObject
class NewClass: UIView { }
But using the new class in a Objective-C class, Xcode can't find this new class, with these errors:
# import "Name-Swift.h" is not recognized.
NewClass *item = [NewClass alloc]; - Unknown type name 'NewClass'
I checked the following:
- The new class is added to all Targets and is verified in the Build-Phases->Compile-Sources section
My other Swift classes don't have public or #objc identifiers, and everything works OK..... So I still I tried using them in the new class (#objc and public in the class definition), with no success
With only adding NewClass *item = [NewClass alloc]; in any Objective-C class makes Xcode to stop recognizing "Name-Swift.h"
Any idea what is missing?
After some testing, I found that I need to import the Name-Swift.h file in the .h file where I want to declare my object.
My project defines the #import Name-Swift.h in the Name-Prefix.pch
#ifdef TEST
#import "Name_Test-Swift.h"
#elif LOCAL
#import "Name_Local-Swift.h"
#else
#import "Name-Swift.h"
#endif
The #import declaration in the .pch file allows the Swift class to be accesible anywhere from the .m file of my Objective-C class, but not from the .h
I am not sure if that is the expected behavior... I will do a deep dive to the Documentation.

declare obj-c class interface that contain c++ class type ivar

Currently I am working on a cocos2d+Box2D project so I have deal with some Objective-C++ code.
And I am facing to such situation:
#import "cocos2d.h"
#import "Box2D.h"
#interface BasicNode : CCNode {
#private
ccColor3B _color;
b2Body *_body;
b2Fixture *_shape;
}
b2Body and b2Fixture are C++ class that defined in Box2D.h
It works if the implementation of BasicNode is named BasicNode.mm.
But if I have another file named Game.m that is using BasicNode and import BasicNode.h, it won't compile because .m file is Obj-C file and does not know about C++ code.
So I decided to move #import "Box2D.h" into implementation file and only keep type declaration in head file (this is exactly what header file should contain).
But how do I do it? They are C++ class type but they are actually just a pointer so I wrote some helper macro
#ifdef __cplusplus
#define CLS_DEF(clsname) class clsname
#else
#define CLS_DEF(clsname) struct clsname; typedef struct clsname clsname
#endif
CLS_DEF(b2Body);
CLS_DEF(b2Fixture);
It works, only if CLS_DEF(b2Body) is appear once only. Otherwise compiler will find multiple type declaration for a same name even they are the same. Than I have to change to
#ifdef __cplusplus
#define CLS_DEF(clsname) class clsname
#else
#define CLS_DEF(clsname) #class clsname
#endif
And it is working now.
But I don't think it is a great idea that I declare a C++ class type as an Obj-C class especially I am using ARC.
Is any better way do deal with it? And I don't really want to make something like this
#interface BasicNode : CCNode {
#private
ccColor3B _color;
#ifdef __cplusplus
b2Body *_body;
b2Fixture *_shape;
#else
void *_body;
void *_shape;
#endif
}
Edit: Also please tell me will my tweak way introduce any problem?? by making C++ class ivar looks like Obj-C class for other pure Obj-C code.
One simple solution is to rename Game.m to Game.mm.
There are a couple of ways. If you can rely on using the Objective-C 2.2 runtime's features, you can add ivars in class (category) extensions. This means you can add ivars in your class's .mm file, and keep the .h file clean of any C++ stuff.
If you need to support older versions of the runtime, there are a few ways to do it which are better than #ifdefing. In my opinion, the best way is to use the 'pimpl' idiom which is common in C++ - you forward declare an implementation struct in your header, and add an ivar which is a pointer to such a struct. In your class's implementation (.mm), you actually define that struct with all its C++ members. You then just need to allocate that implementation object in your init... method(s) with new and delete it in dealloc.
I've written up the pimpl idiom as it applies to cleanly mixing Objective-C and C++ in this article - it also shows some other potential solutions which you could consider.
With Xcode 5, you don't have to declare instance variables in the header file, you can just declare them in the implementation file. So your BasicNode header file is not "contaminated" with C++.
You can use "struct" instead of "class" in C++. The only difference is that in a class all members are private by default, while in a struct they are public by default. But you can do everything with a struct that you can do with a class. That way you can write for example
struct b2Body;
struct b2Fixture;
outside your interface, and
{ ...
struct b2Body* _body;
...
}
in your interface.

Get access function in main.m [iPhone]

I'm doing Universal Application and it has isPad() method inside of main.m class. I want to use this method inside of both AppDelegates (iPad, iPhone).
Yes, it is currently accessible in a AppDelegates
**From AppDelegate
- (void)applicationDidFinishLaunching:(UIApplication *)application
{
if (isPad()) // isPad() method locates main.m and is accessible through delegate.
// but I gets warning message 'Implicit declaration of function isPad()
{
}
}
From main.m
BOOL isPad();
int main(int argc, char *argv[])
{ //detecting iPad and iPhone }
BOOL isPad()
{
// method that detects the device.
}
Why Am I getting warning message 'Implicit declaration of function isPad()?
Do I need to #include main.m inside of AppDelegate.m ? then how to ?
I want to be use this method in any other classes.
[[UIApplication sharedApplication].delegate isPad()];
Yes, I made mistake!!
I have used two delegate one for pad, and other for phone.
It could be done in just using one delegate.
Sorry everyone!
Why have you put your isPad() method in main.m? Why can't you put that method in your shared app delegate, and that way all classes inside your app can access it? I would recommend simply creating your isPad() method inside of your shared app delegate and avoiding putting it in main.m all together.
You are trying to call isPad() function on Objective C object.
You defined your isPad() function inside main(), what's that?
The warning message you receive in the AppDelegate code is because there is no function prototype for isPad. You can get rid of the warning by putting BOOL isPad(); in your AppDelegate implementation file.
I strongly recommend you move this logic out of the main file.

Proper way to structure classes in Xcode framework

I'm building a custom Xcode framework, and I have a class called AXController that has a class method called showActivationWindow. showActivationWindow initializes and shows a window using AXWindowController which is a subclass of NSWindowController. Then, AXWindowController calls activate which is a class method in AXController on a button press in the window it displays.
I included AXWindowController.h in AXController.h and included AXController.h in AXWindowController.h so each could access the other's methods, but this is throwing a lot of Xcode warnings. It also seems like a very inelegant solution.
What is the correct way to solve this problem?
It's not a good idea to import header files recursively. There's a directive #class (link to Apple doc) which tells that there is a class named as such. In practice the usage is something like A.h
#class B;
#interface A:NSObject {
B* anInstanceOfB;
}
...
#end
and B.h
#import "A.h"
#interface B:NSObject {
A* anInstanceOfA;
}
...
#end
Then you can import A.h and B.h as you like from your .m file! But be careful not to make a retain cycle, if you don't use garbage collection.