Learn Objective C(4)Programming with Objective-C - Encapsulating Data and Custom
Learn Objective C(4)Programming with Objective-C - Encapsulating Data and Customizing Existing Classes
3. Encapsulating Data
3.1 Properties Encapsulate an Object's Values
Declare Public Properties for Exposed Data
@interface ZJUPerson : NSObject
@property NSString *firstName
@property NSString *lastName
@end
Use Accessor Methods to Get or Set Property Values
NSString *firstName = [somePerson firstName];
[somePerson setFirstName:@"Carl"];
By default, these accessor methods are synthesized automatically for you by the compiler.
@property (readonly) NSString *fullName;
The opposite of readonly is readwrite, it is default.
@property (getter=isFinished) BOOL finished;
@property (readonly, getter=isFinished) BOOL finished;
Dot Syntax Is a Concise Alternative to Accessor Method Calls
NSString *firstName = somePerson.firstName;
somePerson.firstName = @"Carl";
Getting a value using somePerson.firstName is the same as using [somePerson firstName]
Setting a value using somePerson.firstName = @"Carl" is the same as using [somePerson setFirstName: @"Carl"];
Most Properties Are Backed by Instance Variables
For a property called firstName, the synthesized instance variable will be called _firstName.
- (void) someMethod {
NSString *myString = @"an string";
_someString = myString;
}
_someString is an instance variable.
- (void) someMethod {
NSString *myString = @"an string";
self.someString = myString;
}
You can Customize Synthesized Instance Variable Names
@implementation YourClass
@synthesize propertyName = instanceVariableName;
…snip…
@end
The default is @synthesize firstName = firstName;
But we should always use
@synthesize firstName = _firstName;
You Can Define Instance Variables without Properties
It is best practice to use a property on an object any time you need to keep track of a value.
If we want to define our own instance variables without declaring a property, we can add them insides braces at the top of the class interface or implementation.
@interface SomeClass : NSObject {
NSString *_myNonPropertyInstanceVariable;
}
@end
@implementation SomeClass {
NSString *_anotherCustomInstanceVariable;
}
@end
Access Instance Variables Directly from Initializer Methods
Creating the initialize methods.
- (id)initWithFirstName:(NSString *)aFirstName lastName:(NSString *) aLastName;
- (id)initWithFirstName:(NSString *)aFirstName lastName:(NSString *) aLastName {
self = [super init];
if(self){
_firstName = aFirstName;
_lastName = aLastName;
}
return self;
}
The Designated Initializer is the Primary Initialization Method
We need to define one designated initializer among a lot of initialization methods.
You Can Implement Custom Accessor Methods
@property (readonly) NSString *fullName;
- (NSString *) fullName {
return [NSString stringWithFormat:@"%@ %@", self.firstName, self.lastName];
}
Properties Are Atomic by Default
@interface ZJUObject : NSObject
@property NSObject *firstObject; //atomic by default
@property (atomic) NSObject *secondObject //
@end
From different threads, it is always fully retrieved by the getter method or fully set via the setter method.
atomic -- nonatomic
@interface ZJUObject : NSObject
@property (nonatomic) NSObject *oneobject;
@end
@implementation ZJUObject
- (NSObject *) oneobject {
return _oneobject;
}
//setter will be synthesized automatically
@end
3.2 Manage the Object Graph through Ownership and Responsibility
In Objective-C, an object is kept alive as long as it has at least one strong reference to it from another object.
ZJUPerson Object
@property NSString *firstName; -----strong -----> NSString Object @"Carl"
@property NSString *lastName; ------strong -----> NSString Object @"Luo"
Avoid Strong Reference Cycles
If a group of objects is connected by a circle of strong relationships, they keep each other alive even if there are no strong references from outside the group.
A strong reference circle
NSTableView -------> strong ------> Delegate Object
@property id delegate <------- strong -------- @property NSTableView *tableView;
NSTableView -------> weak ------> Delegate Object
@property (weak) id delegate <------- strong -------- @property NSTableView *tableView;
Use Strong and Weak Declarations to Manage Ownership
By default the references are strong
@property id delegate;
@property (weak) id delegate;
Local variables also maintain strong references to objects by default. If we don't want a variable to maintain a strong reference, we can declare __weak
NSObject *__weak weakVariable;
Use Unsafe Unretained References for Some Classes
@property (unsafe_unretatined) NSObject *unsafeProperty;
NSObject * __unsafe_unretained unsafeReference;
Copy Properties Maintain Their Own Copies
@interface ZJUBadgeView : NSView
@property NSString *firstName;
@property NSString *lastName;
@end
NSMutableString *nameString = [NSMutableString stringWithString:@"John"];
self.badgeView.firstName = nameString;
nameString appendString:@"ny";
The object ZJUBadgeView will change after the append action. So we need a copy of the value for the object in some circumstances.
@interface ZJUBadgeView : NSView
@property (copy) NSString *firstName;
@property (copy) NSString *lastName;
@end
alternatively
- (id) initWithSomeOriginalString:(NSString *) aString {
self = [super init];
if (self) {
_instanceVariableForCopyProperty = [aString copy];
}
return self;
}
4. Customizing Existing Classes
4.1 Categories Add Methods to Existing Classes
@interface ClassName (CategoryName)
@end
At runtime, there is no difference between a method added by a category and one that is implemented by the original class.
Write the Category for ZJUPerson class like this.
#import "ZJUPerson.h"
@interface ZJUPerson (ZJUPersonNameDisplayAddtions)
- (NSString *) lastNameFirstNameString;
@end
A category is usually declared in a separate header file and implemented in a separate source code file. For example
ZJUPerson+ZJUPersonNameDisplayAdditions.h
The category implementation is as follow:
#import "ZJUPerson+ZJUPersonNameDisplayAdditions.h"
@implementation ZJUPerson ( ZJUPersonNameDisplayAdditions)
- (NSString *) lastNameFirstNameString {
return [NSString stringWithFormat: @"%@, %@", self.lastName, self.firstName];
}
@end
After implement the Category, we can use that method both in ZJUPerson class itself or the sub classes.
#import "ZJUPerson+ZJUPersonNameDisplayAdditions.h"
@implementation SomeObject
- (void) someMethod {
ZJUPerson *person = [[ZJUPerson alloc] initWithFirstName:@"Carl"
lastname:@"Luo"];
NSLog(@"The people is %@", [person lastnameFirstNameString]);
}
@end
We can provide different implementations for the category methods, depending on whether I am writing an app for OS X or iOS.
Avoid Category Method Name Clashes
We need to be very careful about method names, if the name of a method declared in a category is the same as a method in the original class, or a method in another category on the same class (or even a superclass), the behavior is undefined as to which method implementation is used at runtime.
To solve the clashes, it is best practice to add a prefix to the method name.
+ (id) zju_sortDescriptorWithKey: (NSString *) key ascending: (BOOL) ascending;
4.2 Class Extensions Extend the Internal Implementation
A class extension bears some similarity to a category, but it can only be added to a class for which you have the source code at compile time.
There is no way to declare a class extension on a framework class.
@interface ClassName ()
@end
Since there is not name in the parentheses, class extensions are often referred to as anonymous categories. We can define the properties then.
@interface ZJUPerson ()
@property NSObject *extraProperty;
@end
Use Class Extensions to Hide Private Information
…snip...
Consider Other Alternatives for Class Customization
…snip...
References:
https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/EncapsulatingData/EncapsulatingData.html#//apple_ref/doc/uid/TP40011210-CH5-SW1