今天讲Objective-C中使用类的继承来隐藏private信息
Use Class Extensions to Hide Private Information
The primary interface for a class is used to define the way that other classes are expected to interact with it. In other words, it’s the public interface to the class.
Class extensions are often used to extend the public interface with additional private methods or properties for use within the implementation of the class itself. It’s common, for example, to define a property as readonly in the interface, but as readwrite
in a class extension declared above the implementation, in order that the internal methods of the class can change the property value directly.
As an example, the XYZPerson class might add a property called uniqueIdentifier, designed to keep track of information like a Social Security Number in the US.
It usually requires a large amount of paperwork to have a unique identifier assigned to an individual in the real world, so the XYZPerson class interface might declare this property as readonly, and provide some method that requests an identifier be assigned,
like this:
@interface XYZPerson : NSObject
...
@property (readonly) NSString *uniqueIdentifier;
- (void)assignUniqueIdentifier;
@end
This means that it’s not possible for the uniqueIdentifier to be set directly by another object. If a person doesn’t already have one, a request must be made to assign an identifier by calling the assignUniqueIdentifier method.
In order for the XYZPerson class to be able to change the property internally, it makes sense to redeclare the property in a class extension that’s defined at the top of the implementation file for the class:
@interface XYZPerson ()
@property (readwrite) NSString *uniqueIdentifier;
@end
@implementation XYZPerson
...
@end
Note: The readwrite attribute is optional, because it’s the default. You may like to use it when redeclaring a property, for clarity.
This means that the compiler will now also synthesize a setter method, so any method inside the XYZPerson implementation will be able to set the property directly using either the setter or dot syntax.
By declaring the class extension inside the source code file for the XYZPerson implementation, the information stays private to the XYZPerson class. If another type of object tries to set the property, the compiler will generate an error.
Note: By adding the class extension shown above, redeclaring the uniqueIdentifier property as a readwrite property, a setUniqueIdentifier: method will exist at runtime on every XYZPerson object, regardless of whether other source code files were aware of the class extension or not.
The compiler will complain if code in one of those other source code files attempts to call a private method or set a readonly property, but it’s possible to avoid compiler errors and leverage dynamic runtime features to call those methods in other ways, such as by using one of the performSelector:... methods offered by NSObject. You should avoid a class hierarchy or design where this is necessary; instead, the primary class interface should always define the correct “public” interactions.
If you intend to make “private” methods or properties available to select other classes, such as related classes within a framework, you can declare the class extension in a separate header file and import it in the source files that need it. It’s not uncommon to have two header files for a class, for example, such as XYZPerson.h and XYZPersonPrivate.h. When you release the framework, you only release the public XYZPerson.h header file.