Circular Header Import, Enum Unrecognized - objective-c

I've got two Cocoa Touch classes (objective-c). And both #import each other's header.
Class A's header defines an enum, which Class B's header is trying to use. Class B fails to compile due to not recognizing the type.
I think this is a circular dependency issue, but I think #class won't be too much help since the issue is with an enum.
What do you suggest?

Put the enum in it's own header and forward declare the classes with #class where needed in each others headers so A don't need to import B and B don't need to import A but booth import the enum header.

in header files use #class ClassName;
and in .m file use import.

Related

Objective C duplicate symbol issue

I am new to objective C.
My app contains 2 view controller. v1 and v2
I need to user a third party SDK in my add. the header is DTDevice.h.
I use this the functions in my v2,so I import DTDevice.h in my v2.h file. However I need to pass a string from v1 to v2. So I import v2.h in v1.h
The app failed built, because one the property in DTDevice.h complain duplicate symbol.
My question is how to pass string from v1 to v2 without reference v2.h inside v1.h.
Thanks very much
Instead of importing, use predeclaring for header files.
For example if you need MyClass object, instead of doing #import "MyClass.h" you can predeclare it by doing #class MyClass. Predeclarations, as well as imports goes before #interface block.
Of course you will have to import MyClass in your .m by doing standard #import "MyClass.h"
As a rule of thumb, try to avoid importing in .h files in favour of predeclaring. For classes use #class for protocols use #protocols. This lets you avoid import cycles.

How can I add forward class references used in the -Swift.h header?

I'm integrating Swift code into a large Objective-C project, but I'm running into problems when my Swift code refers to Objective-C classes. For example, suppose I have:
An Objective-C class called MyTableViewController
An Objective-C class called DeletionWorkflow
I declared a Swift class as follows:
class DeletionVC: MyTableViewController {
let deleteWorkflow: DeletionWorkflow
...
}
If I now try to use this class by importing ProjectName-Swift.h into Objective-C code, I get undefined symbol errors for both MyTableViewController and DeletionWorkflow.
I can fix the problem in that individual source file by importing DeletionWorkflow.h and MyTableViewController.h before I import ProjectName-Swift.h but this doesn't scale up to a large project where I want my Swift and Objective-C to interact often.
Is there a way to add forward class references to ProjectName-Swift.h so that these errors don't occur when I try to use Swift classes from Objective-C code in my app?
You can create another header file that forward declares or imports the necessary classes, and then imports ProjectName-Swift.h. For example, create a file named ProjectName-Swift-Fixed.h with the contents:
// ProjectName-Swift-Fixed.h
// Forward declarations for property classes
#class DeletionWorkflow;
// Imports for superclasses
#import "MyTableViewController.h";
#import "ProjectName-Swift.h"
Then, instead of #import "ProjectName-Swift.h" in your codebase, use #import "ProjectName-Swift-Fixed.h.
This is a little silly, but it sounds like your "workaround" is what Apple intended, at least for now. From the interoperability guide:
If you use your own Objective-C types in your Swift code, make sure to import the Objective-C headers for those types prior to importing the Swift generated header into the Objective-C .m file you want to access the Swift code from.
In this devforums thread, someone mentioned they already filed a bug in Radar. You probably should too.

Having compilation error with #import

I'm having some error on the following relationship
I have 2 classes, Class A and Class B
inside Class A header, it will #import "B.h"
inside Class B header, it will #import "A.h"
I'm having error during compilation. Anyone know how can I resolve this?
Error looks something like this:
expected specifier-qualifier-list before 'GameUILayer'
Instead of importing the headers in the .h files, use forward declarations. So instead of
#import "ClassA.h"
you use:
#class ClassA
etc
and then you use the import statement within the .m files.
See my earlier answer for the link to the documenation.
You're creating a circular dependency. One class should import the other. They can't both import each other. If you're making interacting classes like that, there should be a hierarchy of dependence. B depends on A, A depends on, at a minimum, the root class NSObject. If you make two classes dependent on each other, the compiler can either yell at you or try to compile it and end up running around in circles. Most compilers are designed to yell at you.

Forward-declare enum in Objective-C

I'm having trouble with enum visibility in an Objective-C program. I have two header files, and one defines a typedef enum. Another file needs to use the typedef'd type.
In straight C, I would simply #include the other header file, but in Objective-C, it's recommended not to use #import between header files, instead using forward #class declarations as needed. However, I can't figure out how to forward-declare an enumeration type.
I don't need the actual enumerated values, except in the corresponding .m implementation file, where I can safely #import away. So how can I get the typedef enum to be recognized in the header?
Most recent way (Swift 3; May 2017) to forward declare the enum (NS_ENUM/NS_OPTION) in objective-c is to use the following:
// Forward declaration for XYZCharacterType in other header say XYZCharacter.h
typedef NS_ENUM(NSUInteger, XYZCharacterType);
// Enum declaration header: "XYZEnumType.h"
#ifndef XYZCharacterType_h
#define XYZCharacterType_h
typedef NS_ENUM(NSUInteger, XYZEnumType) {
XYZCharacterTypeNotSet,
XYZCharacterTypeAgent,
XYZCharacterTypeKiller,
};
#endif /* XYZCharacterType_h */`
The answer to your question is to either go ahead and import the typedef header file or to use a generic type like NSInteger instead of the enum type.
However, there is more reason to not importing a header file than just compile speed.
Not importing a header file also reduces your inadvertent access to extraneous classes.
For example, say you have a TrackFileChanges class that tracks the file system for changes to a specific file, and you have a CachedFile class that stores cached data from a file. The latter might use a private ivar of type TrackFileChanges*, but for uses of CachedFile, this is simply an implementation detail (ideally, the ivar would be auto-generated with a private property using the new runtime, but thats not possible if you're using the old run time).
So clients that #import "CachedFile.h" probably do not need or want access to TrackFileChanges.h. And if they do, they should make it clear by #importing it themselves. By using #class TrackFileChanges instea of #import "TrackFileChanges.h" in CachedFile.h you improve the encapsulation.
But all that said, there is nothing awrong with importing a header file from a second header file if the second header wants to expose the first to all clients. For example, header files that declare classes need to be imported directly in subclassing header files, and header files declaring protocols might well be imported directly (although youy can use #protocol ABC; to avoid this).
Go ahead and use #import. The only reason people recommend to use #class when possible is because it makes your code slightly faster to compile. However, there is no issue with #importing one .h file from another. In fact, you need to do this when extending another class.
If you are ok using compiler extensions, you could use this order in Clang:
enum Enum;
typedef enum Enum Enum2;
void f(Enum2); // ok. it sees this type's true name.
enum Enum {
E_1
};
// ok. now its declaration is visible and we can use it.
void f(Enum2 e) {
}
Note: It will trigger a -Wpedantic warning.
If you are using C++11, you should use their enums, which are safe to forward declare -- e.g. enum class Enum:uint8_t; (not a compiler extension).
What worked for a forward declaration of an enum for me in an Objective C .h file was look in the ProjectName-Swift.h file and see what it put, which happened to be the following:
enum SwiftEnumName : NSInteger;
I needed this forward declaration because I had a function parameter type of SwiftEnumName. And it wouldn't let me put the ProjectName-Swift.h import in the Objective C .h file.
Then in the Objective C .m file I just had the #import "ProjectName-Swift.h" in it and just used the SwiftEnum normally.
This was using Swift 4.1.2.
You'd have to either #import them anyway or create a separate header file containing only the typedef. Not importing header files in a header makes the compilation faster, but doesn't change anything else.
Why doesn't C++ support forward declaration of enums?

Objective-C: Importing headers in .h or .m?

I'm new to objective-c and would like to know the best practice for importing some external headers that I use in my class.
Should I be storing the #import "classB.h" in my own classes .h file or in the .m file?
What's the difference?
Thanks!
It is proper practice to put a forward class declaration (#class classB;) in the header and #import "classB.h in the .m
A forward class declaration, like #class classB; lets the compiler know it should expect the class later on, and it shouldn't complain about it at the moment.
To avoid circular references, only #import a header file in another class's header file if it's inheriting from that class. Otherwise, use #class ClassName to declare the class type if you need it in your header file, and #import it in the implementation file.
To the compiler, it really doesn't matter. You could just throw forward declarations in your .h and then wait to #import until your .m file. See this post on SO for more info on this.
From a clean-code prospective, some may argue that putting the imports in your implementation file keeps the details closer to where they are needed (see that link above as well; the people there reference this idea).
It's recommended that you import other header files in your header file. That way you can use the class in both the header and the implementation files (because the implementation file (.m) imports its associated header file).
If you want to know when to import files and when to use forward-class declaration, you can go here. ;-)