用OBJC編程3-Encapsulating Data
@interface XYZPerson :NSObject
@property NSString *firstName;
@property NSString *lastName;
@end
/// ============
NSString *firstName = [somePerson firstName];
[somePerson setFirstName:@"Johnny"];
限定屬性為只讀,也可限定為readwrite,但這不必,因為缺省如是。@property (readonly) NSString *fullname;
可以指定屬性的訪問器名稱,多個限定詞如下格式@property (readonly, getter=isFinished) BOOL finished;
使用點語法NSString *firstName = somePerson.firstName;
// NSString *firstName = [somePerson firstName];
somePerson.firstName = @"Johnny";
// [somePerson setFirstName:@"Johnny"];
大多數屬性有一個實例變量。缺省的讀寫屬性會由編譯器自動生成一個實例變量,以下劃線開始,如_firstName;-(void) someMethod{
NSString *myString = @"An interesting string";
_someString = myString;
// self.someString = myString;
// or
// [self setSomeString:myString];
}
可以指定實例變量的名字@implementation YourClass
@synthesize propertyName = instanceVariableName;
@end
// ---- for example
@synthesize firstName = ivar_firstName;
如果你不指定名字,實例變量則和屬性同名,前面沒有下劃線@synthesize firstName;
如果你并不想提供數值給其它對象,你不必聲明一個屬性而使用一個實例變量@interface SomeClass: NSObject{
NSString *_myNonPropertyInstanceVariable;
}
@end
@implementation SomeClass{
NSString *_anotherCustomInstanceVariable;
}
在初始化方法里訪問實例變量Setter方法會有附加效果。它們可能觸發KVC通知,或者完成你定制的方法。你應該在初始化方法里直接訪問實例變量,因為對象還沒有初始化完成。甚至你不應該提供定制的訪問器方法給你的類提供附加效果。這樣將來的子類可以很好的override這個行為。一個典型的init方法如下-(id)init{
self = [super init];
if(self){
// initialize instance variables here
}
return self;
}
可以指定初始化方法-(id)initWithFirstName:(NSString *)aFirstName lastName:(NSString *)aLastName{
self = [super init];
if(self){
_firstName = aFirstName;
_lastName = aLastName;
}
return self;
}
可以指定訪問方法@property (readonly) NSString *fullName;
// -------------
-(NSString *)fullName{
return [NSString stringWithFormat:@"%@ %@", self.firstName, self.lastName];
}
如果你需要在訪問器里訪問實例變量,那應該直接訪問。例子里延遲初始化一個對象,lazy accessor。- (XYZObject *)someImportantObject {
if(!_someImportantObject){
_someImportantObject = [[XYZObject alloc] init];
}
return _someImportantObject;
}
編譯器會自動synthesize一個實例變量。至少一個訪問方法。如果你為readwrite屬性實現了getter和setter,或者為readonly實現了getter。編譯器認為你想控制屬性實現,也不會再為你自動生成一個實例變量。因此,如果你仍然需要一個實例變量,你需要手動synthesize@synthesize property = _property;
屬性缺省是原子性的。atomic@interface XYZObject : NSObject
@property NSObject *implicitAtomObject; // 缺省是atomic
@property (atomic) NSObject *explicitAtomicObject; // 指明atomic
@end
缺省訪問器已經解決了多線程并發的問題。如果你定制了一個atomic, readwrite的屬性的setter,而讓編譯器自動生成getter,將會得到一個編譯時警告。你可以聲明nonatomic屬性,因為不需要guarantee,處理并發,因此它的訪問器比atomic屬性更快。屬性的原子性并不意味著對象是線程安全的。例如firstName和LastName。管理對象的生命周期,對象是通過指針來訪問,內存是動態申請的,指針變量的生命周期不代表對象的證明周期。strong reference意味著對象和另一個對象的生命周期一樣長。屬性缺省是強引用,可以指定weak。本地變量都是強引用,如果你不希望維護一個強引用,可以使用__weak@property (weak) id delegate;
// ---------
NSObject * __weak weakVariable;
弱引用會帶來不安全的行為,因為變量可能會被置為nil。一些Cocoa類不能聲明為弱引用,包括NSTextView, NSFont, NSColorSpace等,如果你需要使用這些類的一個弱引用,你需要一個unsafe_unretained聲明。@property (unsafe_unretained) NSObject *unsafePropery;
// ------------
NSObject * __unsafe_unretained unsafeReference;
unsafe引用類似weak引用,但當對象釋放時,它不會被置為nil,因此你可能會持有一個懸掛指針,指向一個未知內存,向它發消息可能會導致崩潰。copy屬性@interface XYZBadgeView : NSView
@property NSString *firstName;
@peoperty NSString *lastName;
@end
如果你這樣做NSMutableString *nameString = [NSMutableString stringWithString:@"John"];
self.badgeView.firstName = nameString;
// ----
[nameString appendString:@"ny"];
這樣firstName將指向一個NSMutableString,它的值可以改變了,你可以增加copy聲明,避免這種情況@interface XYZbadgeView : NSView
@property (copy) NSString *firstName;
@property (copy) NSString *lastName;
@end
// --------------------
NSMutableString *nameString = [NSMutableString stringWithString:@"John"];
self.badgeView.firstName = nameString;
// ----
[nameString appendString:@"ny"];
這樣firstName仍然是“John”,不會發生變化一個被聲明為copy的對象必須支持NSCopying協議。如果你要直接set一個copy屬性的實例變量,例如在初始化方法里,一定要設置原始對象的copy-(id)initWithSomeOriginalString:(NSString *)aString{
self = [super init];
if(self){
_instanceVariableForCopyProperty = [aString copy];
}
return self;
}