在对象内部尽量直接访问实例变量
在对象内部读取数据时,应该直接通过实例变量来读,而写入数据时,则应通过属性来写。
_name = @"Jack"
不经过setter的消息发送,直接为变量赋值,速度快。
对于以下的 name 属性:@property (nonatomic, copy) NSString *name;
直接赋值是:_name = @"Jack"
; ,通过self.name = @"Jack"
其实等同于_name = @"Jack".copy
。self.name = @"Jack"
会触发KVO,_name = @"Jack"
不会
-self.name = @"Jack"
可以在 setter 方法中进行断点调试,每次赋值你都知道。- 所以有一种合理折中方案就是,读取数据的时候用
NSString *str = _name
,赋值用self.name = @"Jack"
。
在对象内部访问实例变量时,是通过属性(self.proper
)来访问还是通过_proper
来访问区别在于是否执行属性的setter
、getter
方法。
-
如果执行属性的
setter
、getter
方法,则通过_proper
来访问。 -
如果未执行属性的
setter
、getter
方法,则通过属性(self.proper
)来访问。
@interface Wrestler : NSObject
@property (copy, nonatomic) NSString *name; // 将name声明为属性
- (void)smell;
@end
@implementation Wrestler
@synthesize name = _name; // 属性name可以使用实例变量_name直接访问
- (void)setName:(NSString *)aName {
NSLog(@"Set name");
_name = [aName copy];
}
- (NSString *)name {
NSLog(@"Get name");
return [_name copy];
}
- (void)smell {
NSLog(@"*** Smelling ***");
// 使用dot syntax访问实例变量
NSLog(@"%@", self.name);
// 直接调用属性的getter方法
NSLog(@"%@", [self name]);
在初始化方法及dealloc
方法中,总是应该直接通过实例变量来读写数据。
-
子类可能复写
setter
方法,用self.proper = @""
可能不等同于_proper = @"".copy
。 -
我们写一个
Wrestler
的子类Cena
,该类继承了属性name
并重写了其setter
方法,该方法会先检验名字后缀是否为Cena
,否则抛出异常。
@interface Cena : Wrestler
- (instancetype)initWithName:(NSString *)aName;
- (void)wrestle;
@end
@implementation Cena
@synthesize name = _name;
- (instancetype)initWithName:(NSString *)aName {
self = [super init];
if (self) {
NSLog(@"self.name = aName");
self.name = aName;
}
return self;
}
- (void)wrestle {
NSLog(@"I'm %@, U can't see me", self.name);
}
- (void)setName:(NSString *)aName {
if (![aName hasSuffix:@"Cena"]) {
[NSException raise:NSInvalidArgumentException format:@"last name must be Cena"];
}
_name = [aName copy];
}
@end
- 在父类Wrestler的init方法中将name初始化为空白字符串@""
(instancetype)init {
self = [super init];
if (self) {
NSLog(@"self.name = empty string");
self.name = @"";
}
return self;
- 调用
Cena *cena = [[Cena alloc] initWithName:@"John Cena"];
[cena wrestle];
- 运行崩溃。原因:
self.name = @"";
。调用子类中覆写的name
的setter
方法,空白字符串明显没有@"Cena"
后缀,从而抛出异常。
使用Lazy Initialization
配置的数据,应该通过属性来读取数据。
@property (strong, nonatomic) NSNumber *chamCount;
- (NSNumber *)chamCount {
if (!_chamCount) {
_chamCount = @13;
}
return _chamCount;
不要在setter/getter方法中调用setter/getter
方法
- 将上面的setter方法修改:
- (void)setName:(NSString *)aName {
NSLog(@"Set name");
// _name = [aName copy];
self.name = aName;
}
- 运行程序,控制台不停输出
Set name
,崩溃。 - 原因:在
setter
方法中调用setter方法会不断嵌套调用,最终导致程序崩溃。getter方法同理。
要点
-
在对象内部读取数据时,应该直接通过实例变量来读,而写入数据时,则应通过属性来写。
-
在初始化方法及dealloc方法中,总是应该直接通过实例变量来读写数据。
-
使用Lazy Initialization配置的数据,应该通过属性来读取数据。
-
不要在setter/getter方法中调用setter/getter方法。