欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

52个有效方法(7) - 在对象内部尽量直接访问实例变量

程序员文章站 2023-11-14 18:48:04
1. 在对象内部读取数据时,应该直接通过实例变量来读,而写入数据时,则应通过属性来写。 2. 在初始化方法及dealloc方法中,总是应该直接通过实例变量来读写数据。 3. 使用Lazy Initialization配置的数据,应该通过属性来读取数据。 4. 不要在setter/g... ......

在对象内部尽量直接访问实例变量

在对象内部读取数据时,应该直接通过实例变量来读,而写入数据时,则应通过属性来写。

  • _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来访问区别在于是否执行属性的settergetter方法。

  • 如果执行属性的settergetter方法,则通过_proper来访问。

  • 如果未执行属性的settergetter方法,则通过属性(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 = @"";。调用子类中覆写的namesetter方法,空白字符串明显没有@"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方法同理。

要点

  1. 在对象内部读取数据时,应该直接通过实例变量来读,而写入数据时,则应通过属性来写。

  2. 在初始化方法及dealloc方法中,总是应该直接通过实例变量来读写数据。

  3. 使用lazy initialization配置的数据,应该通过属性来读取数据。

  4. 不要在setter/getter方法中调用setter/getter方法。