This is the third is a series of blog posts I’m writing on things that Objective-C can learn from Java. The other parts can be found here:
- Part 1 (Generics)
- Part 2 (Abstract Classes)
- Part 3 (Single Source File)
- Part 4 (Namespace)
- Part 5 (Exceptions)
Objective-C still retains a lot of its heritage from it’s C beginnings. This includes using two files, a header and a source file, for each class. In a strictly object oriented environment, the header file contains the class definition (super-class and instance variables), public property definitions, and any public function declarations. The source file contains all of the function implementations, including synthesize statements. In contrast, Java contains all the functions of both files in a single file. To one who knows better, as in one who has used the single file environment, the two files for each class becomes a pain.
As to why this is a pain, one needs only to visit the following scenario: Add a property called
name to the
Person object. In Objective-C, one needs to add the instance variable to the class definition and the property declaration to the header file. Then, add a
@synthesize statement to the implementation and add
[name release]; in the dealloc (I’m neglecting Objective-C’s ability to create the instance variable in modern runtimes since it denies direct access to it in the code). In contrast, Java needs the instance variable added to the class, and in the same file add the setters and getters (which if using Eclipse, can be auto-generated). Java is able to accomplish this because it uses a two-pass compiler. One pass parses the class definition and function declarations, and the second generates the actual code. Any
import statements only need the first pass of each file.
In Objective-C, some of this pain can be eliminated using my fork of ObjectiveCAnnotate, but this is only part of the way to what should be done. What Objective-C really needs is to use a single source file, and auto-generate the equivalent header file when the source file is saved (and save it only if it is different to reduce unnecessary re-compiles). This serves as the equivalent of Java’s first complier pass. Then, the actually compile step executes the second pass, using the generated header and the code within the source file. Using the paradigm, several changes should be made to the source file. The obvious is adding a public (or maybe @public) to functions that should be included in the header file. Additionally, the property syntax can be combined with the instance variable declaration and possibly with synthesize to create something like
@property (nonatomic, retain, synthesize) NSString *name to take the place of the instance variable declaration, the property declaration, and the synthesize statement. Since this is by far the most common use case, why not simplify it to make things easier for the developer? The instance variable still needs to be in dealloc, but now changes in instance variables has been reduced to two places.
This change doesn’t completely eliminate the need for header files. The old C-style declarations, such as C-functions, structs, enums, externs, typedefs, and defines, would still need to be in a header file on their own. Since most classes do not make these declarations, most classes wouldn’t need a distinct header file. In the cases where such a definition is made, it is often better style to include all of the definitions in a single types header file for the framework/project.
This does necessitate a change to the import statements for a class. In the two file environment, one can separate the import statements between the header and source files, but in a single file, this separation no longer exists. However, the compiler could be made smart enough to track the locations of types it inserts in the generated header file, and include those location in the header file’s import statements. This would mean the header file would contain the import statements for the super-class, and any C-style declarations only. There is no need at all to include the import statements for other classes, as the existing
@class declaration tells the compiler all it needs to know at this stage. This would also eliminate the bad coding practice of including unnecessary import statements in the header file, a practice which I’ve seen too often by new programmers who were never taught of its dangers.
Impact on Runtime: None, this is pre-processor only
Impact on Code: Reduction in code complexity, ease in modification. Reduction in need to reference two files to understand a single class.
ingconti - Mar 8, 2011
I agree obj can be better, but in my opinion dividing declaration from imlelmentation is a BIG strength of c++ / obj. I do not need to read all java file to know the methods.
Graham Booker - Mar 8, 2011
ingconti, I guess you missed where I stated the IDE could “auto-generate the equivalent header file when the source file is saved.” Java actually employs a two-pass compiler, one that scans it and creates the equivalent of header file information, and another that does the actual compilation. I’m advocating that Obj-C adopt the same principle, which actually would result in the similar compile times. As per human reading, almost all of that would be eliminated if Obj-C would adopt Javadoc and Xcode had code completion that actually worked properly.
Erik - Aug 9, 2012
This bugged me a lot in C++, but I am totally fine with it in Objective-C since there is much less that needs to be copied each place. You do not have to write private methods or methods that are overridden in header. Properties only have to be written in header now. Copy paste from header is easier than in C++ because you do not need to prefix with classname. As others have mentioned I prefer looking at my interface in the header file. I prefer that over java do or IDE. I get one big screen that I can access very quickly to get an overview over the public API and I can jump quickly between methods. In the IDE I often jump to the header file first and then jump to implementation from there. I guess an auto generated header file would be nice, but it is not on top of my list of things to change with Objective-C. I think ObjC is almost where it needs to be and that improvements would better be done with another language built on top of the Objective-C runtime which does not need to maintain backwards compatibility with C.
Graham Booker - Aug 9, 2012
“Properties only have to be written in header now.” So, you’ve never used private instance variables or public methods which are not setter/getters for properties? There’s plenty that goes in the header file. You mentioned jumping between files, but that brings to light a critical question. Say you are viewing code that makes a function call and you want to see that function. Should the IDE jump to the implementation or the declaration? Heaven forbid you want to go to an implementation in a subclass in Xcode, but I digress. I mentioned ObjAnnotate in my post, and I never do an Obj-C project without it. I strongly believe that this needs to be integrated into the IDE because it saves a lot of busywork that the language requires. Don’t forget that even when using the property declaration without the instance variable, there’s still the @synthesize declaration and renaming or removing a property requires the same to be done in the other file. Why make a human do busywork that a compiler or IDE can do for you? Interesting that you should mention building a language on top of the Obj-C runtime when the first four of these posts have little to no impact on the runtime.
T. Benjamin Larsen - Nov 9, 2013
I think the setup in Objective-C 2.0 is generally sound. The headers should now solely be used for public properties and methods. This makes the header (in most cases) a nice piece of documentation of the class with as little cruft as possible. Technically you don’t HAVE to use header classes, but it’s a nice way to separate a class’ interface from its innards.