I discussed Java for several weeks, but in my job, the primary language I used is Objective-C in the recent one year. When I was still at school, I studied Objective-C for fun, but never used it in real projects. Thus, I didn't realize the interesting part of Objective-C. After I used Objective-C to develop projects in my job, I found that Objective-C is a charming language. It is a static-typed and complied language, but any message sending (calling a method) is determined at runtime, therefore, it also provides many features usually visible in the dynamic languages. It is an object-oriented language, and has
@protocol (as the Interface in UML) and
@interface (as the Class in UML), but does not provide obvious mechanism to control the access of an encapsulation, for example, the today's topic: protected methods.
To discuss the topic is because I am pair programming with two programmers new to Objective-C, and both of them asked me the same question: how to declare and use protected methods in Objective-C? For Java, C++, or C# programmers, using protected methods is just as nature as drinking water. I was not used to the absence of protected methods in Objective-C (although Objective-C has
Figure 1 is a common-seen class diagram. In the figure, the orange dash line represents a boundary -- inside the boundary is a system or a package, and outside the boundary is the outer system. In the package,
AbstractClass implements the
interfaceMethod() method of the
SomeInterface interface, and then add an abstract method
AbstractClass to implement
protectedMethodB(). Then, in the outer system,
Context uses the
ConcreteClass and a customized
CustomizedClass. In such a design,
Context can only see two methods:
AbstractClass can see all methods. For
CustomizedClass, the methods other than
privateMethod() are visible.
Figure 1 - A class hierarchy
The programming languages trends to be dynamic languages. Therefore, the visibility is usually checked at compile time. Using Java as an example, calling private methods directly in code will get a compiler error, but with the reflection API, calling private methods indirectly is allowed. Same as Objective-C, the methods declared in the Header file are public methods, and the methods only placed in the Implementation file are private methods, but both can be invoked through the method
objc_msgSend provided in Objective-C Runtime. Therefore, the simulation discussed here is to simulate the visibility of protected methods only at compile time.
The simulation uses the extension feature -- separate the header file into two parts. One part is AbstractClass.h as shown in Code List 2 that imports the
SomeInterface in Code List 1 and declares the
publicMethod() method. Another part is AbstractClass_Protected.h that declares the
protectedMethodB() methods as shown in Code List 3. Then, in Code List 4, AbstractClass.m imports AbstractClass_Protected.h and implements the
privateMethod() methods. Objective-C does not have the concept of abstract methods, so AbstractClass.m throws exception to simulate the abstract methods.
So far so good. And then discuss how to make the
protectedMethoB() methods visible in the inherted classes.
ConcreteClass imports AbstractClass_Protected.h, overrides the
protectedMethodB() method, as shown in Code List 5 and Code List 6, and in the overridden method
publicMethod(), both the methods
protectedMethodB() can be used directly. The problem seems to be solved. Well, only a part of problem is solved. Another problem is derived from the extension: should I open AbstractClass_Protected.h to the outer system?
Like C++ language, a class is separated into a header file and a implementation file. Although the header file exposes the private methods (variables) to the user, the compiler will check the legality of the method access. In C# or Java language that does not have header files, the visibility is compiled into the bytecode. So while using a packed DLL file or JAR file, the visibility of a method can be checked by the compiler. However, the visibility of a method is Objective-C is determined by whether the header file that have the metod delcaration can be accessed or not. That is if AbstractClass_Protected.h is opened to the outer system, not only
CustomizedClass can see the
protectedMethodB methods, but also
Context. Otherwise, neither
Context can see the protected methods.
Table 1 lists the comparsion of the protected methods visibility simulated by opened and closed extension. In the table, 'o' means visible, and 'x' means invisible. The first column lists the different class, the second column lists the expected visibility (protected methods are invisible only in
Context), the third column lists the actual visibility if the extension is opened (protected methods are visible in all classes), and the forth column lists the actual visibility if the extension is closed to outer system (protected methods are invisible in the
CustomizedClass). It is unfortunate that the extension can simulate the protected methods, but not perfectly. The trade-off to open or not open the extension depends on the actual situation. In fact, opening the extension header file is the same as declaring the protected methods in the original header file, so I prefer not to open the extension header file.
Table 1 - Protected methods visibility comparison
|Class||Expected||Opened Extension||Closed Extension|