用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;
}