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.