美文网首页
笔记-第7条 在对象内部尽量直接访问实例变量(规范)

笔记-第7条 在对象内部尽量直接访问实例变量(规范)

作者: rachel_rui | 来源:发表于2018-08-08 14:00 被阅读0次

    1、除了几种特殊情况之外,笔者强烈建议大家在读取实例变量的时候采用直接访问的形式,而在设置实例变量的时候通过属性来做。

    2、请看下面这个类:

        1.  @interface EOCPerson : NSObject  
        2.  @property (nonatomic, copy) NSString *firstName;  
        3.  @property (nonatomic, copy) NSString *lastName;  
        4.   
        5.  // Convenience for firstName + " " + lastName:  
        6.  - (NSString*)fullName;  
        7.  - (void)setFullName:(NSString*)fullName;  
        8.  @end  
        9.  // fullName与setFullName这两个“便捷方法”可以这样来实现:  
        10. - (NSString*)fullName {  
        11.     return [NSString stringWithFormat:@"%@ %@",  
        12.             self.firstName, self.lastName];  
        13. }  
        14.  
        15. /** The following assumes all full names have exactly 2  
        16. *  parts. The method could be rewritten to support more  
        17. *  exotic names.  
        18. */  
        19. - (void)setFullName:(NSString*)fullName {  
        20.     NSArray *components =  
        21.         [fullName componentsSeparatedByString:@" "];  
        22.     self.firstName = [components objectAtIndex:0];  
        23.     self.lastName = [components objectAtIndex:1];  
        24. } 
    

    在fullName的获取方法与设置方法中,我们使用“点语法”,通过存取方法来访问相关实例变量。现在假设重写这两个方法,不经由存取方法,而是直接访问实例变量:

        1.  - (NSString*)fullName {  
        2.      return [NSString stringWithFormat:@"%@ %@",  
        3.              _firstName, _lastName];  
        4.  }  
        5.   
        6.  - (void)setFullName:(NSString*)fullName {  
        7.      NSArray *components =  
        8.          [fullName componentsSeparatedByString:@" "];  
        9.      _firstName = [components objectAtIndex:0];  
        10.     _lastName = [components objectAtIndex:1];  
        11. } 
    

    这两种写法有几个区别:
    1)由于不经过Objective-C的“方法派发”(method dispatch,参见第11条)步骤,所以直接访问实例变量的速度当然比较快。在这种情况下,编译器所生成的代码会直接访问保存对象实例变量的那块内存。
    2)直接访问实例变量时,不会调用其“设置方法”,这就绕过了为相关属性所定义的“内存管理语义”。比方说,如果在ARC下直接访问一个声明为copy的属性,那么并不会拷贝该属性,只会保留新值并释放旧值。
    3)如果直接访问实例变量,那么不会触发“键值观测”(Key-Value Observing, KVO)通知。这样做是否会产生问题,还取决于具体的对象行为。
    4)通过属性来访问有助于排查与之相关的错误,因为可以给“获取方法”和/或“设置方法”中新增“断点”(breakpoint),监控该属性的调用者及其访问时机。

    3、有一种合理的折中方案,那就是:在写入实例变量时,通过其“设置方法”来做,而在读取实例变量时,则直接访问之。此办法既能提高读取操作的速度,又能控制对属性的写入操作。之所以要通过“设置方法”来写入实例变量,其首要原因在于,这样做能够确保相关属性的“内存管理语义”得以贯彻。但是,选用这种做法时,需注意几个问题。

    4、第一个要注意的地方就是,在初始化方法中应该如何设置属性值。这种情况下总是应该直接访问实例变量,因为子类可能会“覆写”(override)设置方法。假设EOCPerson有一个子类叫做EOCSmithPerson,这个子类专门表示那些姓“Smith”的人。该子类可能会覆写lastName属性所对应的设置方法。

        1.  - (void)setLastName:(NSString*)lastName {  
        2.      if (![lastName isEqualToString:@"Smith"]) {  
        3.          [NSException raise:NSInvalidArgumentException  
        4.                      format:@"Last name must be Smith"];  
        5.      }  
        6.      self.lastName = lastname;  
        7.  } 
    

    在基类EOCPerson的默认初始化方法中,可能会将姓氏设为空字符串。此时若是通过“设置方法”来做,那么调用的将会是子类的设置方法,从而抛出异常。但是,某些情况下却又必须在初始化方法中调用设置方法:如果待初始化的实例变量声明在超类中,而我们又无法在子类中直接访问此实例变量的话,那么就需要调用“设置方法”了。

    5、另外一个要注意的问题是“惰性初始化”(lazy initialization)。在这种情况下,必须通过“获取方法”来访问属性,否则,实例变量就永远不会初始化。

    要点
    ① 在对象内部读取数据时,应该直接通过实例变量来读,而写入数据时,则应通过属性来写。
    ② 初始化方法及dealloc方法中,总是应该直接通过实例变量来读写数据。
    ③ 有时会使用惰性初始化技术配置某份数据,这种情况下,需要通过属性来读取数据。

    相关文章

      网友评论

          本文标题:笔记-第7条 在对象内部尽量直接访问实例变量(规范)

          本文链接:https://www.haomeiwen.com/subject/vcotbftx.html