zoukankan      html  css  js  c++  java
  • 在对象内部尽量直接訪问实例变量 --Effictive Objective-C 抄书

    在对象之外訪问实例变量时,应该总是通过属性来做.在那么在对象内部訪问实例变量的时候,又该怎样呢?

    这是 OCer们一直激烈讨论的问题.有人觉得,不管什么情况,都应该通过属性来訪问实例变量;也有人说,”通过属性訪问”和”直接訪问”应该搭配着用. 除了几种特殊情况之外, 笔者强烈建议大家在读取实例变量的时候採用直接訪问的形式,而在设置实例变量的时候通过属性来做.

    请看以下的类:

    @interface EOCPerson : NSObject
    
    @property(nonatomic,copy)NSString *firstName;
    
    @property(nonatomic,copy)NSString *lastName;
    
    //设置全名的快捷方法
    
    -(NSString*)fullName;
    -(void)setFullName:(NSString*)fullName;
    
    @end
    
    

    fullName和 setFullName这两个”便捷方法”,能够这样来实现:

    -(NSString*)fullName
    {
        return [NSString stringWithFormat:@"%@ %@",self.firstName,self.lastName];
    
    }
    
    /**
     *  以下的方法假设全部的全名有且仅有两部分,当然这种方法也能被改写,来支持外来姓名
     */
    -(void)setFullName:(NSString *)fullName
    {
        NSArray* components = [fullName componentsSeparatedByString:@" "];
        self.firstName = [components objectAtIndex:0];
        self.lastName  = [components objectAtIndex:1];
    }
    
    

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

    -(NSString*)fullName
    {
        return [NSString stringWithFormat:@"%@ %@",_firstName,_lastName];
    }
    
    
    -(void)setFullName:(NSString *)fullName
    {
        NSArray *components = [fullName componentsSeparatedByString:@" "];
        _firstName = [components objectAtIndex:0];
        _lastName  = [components objectAtIndex:1];
    }

    这两种写法有几个差别:

    • 由于不经过 OC的 方法派发( method dispatch ),所以直接訪问实例变量的速度当然比較快. 在这样的情况下,编译器所生成的代码会直接訪问对象实例变量的那块内存.

    • 直接訪问实例变量时,不会调用其设置方法. 这就绕过了为相关属性所定义的”内存管理定义”.比方,在ARC下直接訪问一个声明为 copy的属性,那么并不会copy该属性,仅仅会保留新值并释放旧值.

    • 假设直接訪问实例变量,就不会触发 KVO通知,这样做是否会产生问题,还取决于详细的对象行为.

    • 通过属性来訪问有助于排查与之相关的错误,由于能够setter加入断点,监控该属性的调用者以及訪问时机.

    有一种合理的这样的方案,那就是:在写入实例变量时,通过其 setter来做,而在读取实例变量的时候,直接訪问之.这样,就技能提高读取操作的速度,又能监控对属性的写入操作.之所以要通过setter来写入实例变量,其首要原因在于,这样做能够确保相关属性的”内存管理定义”得以贯彻.可是,选用这样的方法时,须要注意几个问题.

    第一个要注意的地方是,在初始化方法中,应该怎样设置属性值.这样的情况下总是应该直接訪问实例变量,由于子类可能会 覆写(override)设置方法.
    在上例中,假设EOCPerson有一子类叫做 EOCSmithPerson,这个类表示那些姓 Smith 的人.该子类可能会override lastName所相应的设置方法:

    -(void)setLastName:(NSString *)lastName
    {
        if (![lastName isEqualToString:@"Smith"]) {
            [NSException raise:NSInvalidArgumentException format:@"Last name must be Smith "];
        }
        self.lastName  = lastName;
    }
    

    在父类 EOCPerson的默认初始化方法中,可能会将姓氏设为空字符串.此时若是通过 setter方法来做,那么调用的将是子类的设置方法,从而抛出异常.可是某些情况下有必须在初始化方法中调用该设置方法:假设待初始化的实例变量声明在父类中,而我们又无法在子类中直接訪问此实例变量的话,就须要调用 setter 了.

    还有一个要基本的问题是:懒载入.在这样的情况下,必须通过 getter訪问属性,否则实例变量就永远不会初始化.比方,EOCPerson类或许会用一个属性来表示人脑中的信息,这个属性所代指的对象相当复杂.由于此属性不经常使用,并且创建成本较高,所以,我们会在 getter中对其进行懒载入.

    -(EOCBrain*)brain
    {
        if(!_brain)
        {
            _barin = [Brain new];
        }
        return brain;
    }
    

    在这样的情况下,假设没有使用 getter 方法,而直接訪问实例变量,则会看到没有初始化的 brain ,所以说,假设使用了懒载入,就必须通过getter 来訪问brain属性.

    归纳:

    • 在对象内部读取数据时候,应该通过实例变量来读,而写入数据是,则应该通过属性来写.

    • 在初始化以及 dealloc方法中,总是应该通过实例变量来读写数据

    • 有时会使用懒载入技术配置某些数据,这样的情况下,须要通过属性来读取数据.

  • 相关阅读:
    【转】Skynet之消息队列
    [转]Skynet之斗转星移
    [转]skynet Lua中的协程
    win7 + nginx + php
    LexAndYacc 安装程序
    ubuntu + samba 共享失败
    [转]TDD之Dummy Stub Fake Mock
    【转】 纯技术帖:MMOG网络同步算法揭秘
    [转]帧锁定同步算法
    [转]游戏中的同步
  • 原文地址:https://www.cnblogs.com/lytwajue/p/7137639.html
Copyright © 2011-2022 走看看