Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Forward Declaration vs #import when subclassing

I have MyClassA which has a property of type MyClassB

//
//  MyClassA.h
//  

@interface MyClassA : NSObject

@property (strong, nonatomic, readonly) MyClassB *myClassB;

@end

MyClassB has a property myString.

//
//  MyClassB.h
//  

@interface MyClassB : NSObject

@property (copy, nonatomic, readonly) NSString *myString;

@end

I have MyClassC which needs to access myString in it's implementation.

Should I -

a) Forward Declare MyClassB in MyClassA.h and #import "MyClassB.h" in MyClassC.m

or

b) #import MyClassB.h in MyClassA.h

like image 696
Ríomhaire Avatar asked Sep 01 '16 20:09

Ríomhaire


2 Answers

In general, you should forward declare with @class where possible in your header files. The only time you probably wouldn't want to do it is when you're inheriting from a super class or declaring protocol conformance, because the compiler needs to know what is going on in that class or protocol.

For this instance, I would use @class for all your property declarations in your header files, and #import MyClassB.h in your MyClassC.m file. That will allow MyClassC to know about all the properties on MyClassB.

like image 194
Tyler Avatar answered Sep 29 '22 22:09

Tyler


Looking at this from a slightly different angle ... you need to decide if you want the world to really know about myClassB being a property of MyClassA. For example, if you may only want to advertise that myString that can be obtained through MyClassA. This insulates other classes from knowing the underlying implementation of myString. Unless you have a need to expose MyClassB you should hide it from the "rest of the world".

In this case you would change MyClassA.h as follows:

//
//  MyClassA.h
//  

@interface MyClassA : NSObject

@property (strong, nonatomic, readonly) NSString *myString;

@end

In MyClassA.m, you would do the following.

//
//  MyClassA.m
//  

#import "MyClassA.h"
#import "MyClassB.h"

@interface MyClassA()

@property (strong, nonatomic) MyClassB *myClassB;;

@end

@implementation MyClassA

// Other meaningful code omitted

- (NSString *)myString {
    return self.myClassB.myString;
}

@end

Note that what I've done here is use an anonymous category to internally define property for myClassB.

The key thing here is whether or not it makes sense to not expose MyClassB to others. The main advantage of this approach is your code is more malleable. Let's say myString gets derived a different way. From a different class or different method altogether. The code which needs to consume myString is immunized.

If you need to expose MyClassB, then you can either use @class as recommended by Tyler above or #import MyClassB.h from MyClassA.h. Best practices prescribe forward declaring @class. But at times the convenience of not having to remember to import a lot of files within the implementation file can win out. It's your code-base, so you can pick which one works the best for you. I generally use a combination of the two.

like image 41
Mobile Ben Avatar answered Sep 29 '22 21:09

Mobile Ben