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

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

请看以下的类:

  1. @interface EOCPerson : NSObject
  2. @property(nonatomic,copy)NSString *firstName;
  3. @property(nonatomic,copy)NSString *lastName;
  4. //设置全名的快捷方法
  5. -(NSString*)fullName;
  6. -(void)setFullName:(NSString*)fullName;
  7. @end

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

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

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

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

这两种写法有几个差别:

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

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

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

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

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

第一个要注意的地方是,在初始化方法中,应该怎样设置属性值.这样的情况下总是应该直接訪问实例变量,由于子类可能会 覆写(override)设置方法.

在上例中,假设EOCPerson有一子类叫做 EOCSmithPerson,这个类表示那些姓 Smith 的人.该子类可能会override lastName所相应的设置方法:

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

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

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

  1. -(EOCBrain*)brain
  2. {
  3. if(!_brain)
  4. {
  5. _barin = [Brain new];
  6. }
  7. return brain;
  8. }

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

归纳:

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

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

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

在对象内部尽量直接訪问实例变量 --Effictive Objective-C 抄书的更多相关文章

  1. Effective Objective-C 2.0 — 第七条:在对象内部尽量直接访问实例变量

    直接访问实例变量,不经过”方法派发“(method dispatch) 速度快. 直接访问实例变量,不会调用其“设置方法”,这就绕过了为相关属性所定义的“内存管理语义”. 直接访问实例变量,不会触发“ ...

  2. juniper 550M訪问自身公网IP回流内部IP

    拓扑图示意: 网关设备juniper 550M, untrust 区: 公网地址段22.22.22.22/29 trust区:      内部员工PC地址:172.16.4.x /24 trust区: ...

  3. 使用FREDATED引擎实现跨实例訪问

    跨数据库server.跨实例訪问是比較常见的一种訪问方式,在Oracle中能够通过DB LINK的方式来实现. 对于MySQL而言,有一个FEDERATED存储引擎与之相相应.相同也是通过创建一个链接 ...

  4. 设计模式入门之訪问者模式Visitor

    //訪问者模式定义:表示一个作用于某对象结构中的各个元素的操作,它使你能够在不改变各元素类的前提下定义作用于这些元素的新操作. //从定义上看.这个模式跟装饰模式的定义非常类似(动态地给一个对象加入一 ...

  5. Java千百问_03基本的语法(001)_局部变量、类变量、实例变量有什么差别

    点击进入_很多其它_Java千百问 局部变量.类变量.实例变量有什么差别 在聊局部变量.类变量.实例变量有什么差别之前,我们须要了解一下Java变量. 1.Java变量是什么 在数学世界中,我们知道有 ...

  6. 《Java设计模式》之訪问者模式

    訪问者模式是对象的行为模式.訪问者模式的目的是封装一些施加于某种数据结构元素之上的操作.一旦这些操作须要改动的话,接受这个操作的数据结构则能够保持不变. 分派的概念 变量被声明时的类型叫做变量的静态类 ...

  7. OC:属性的内部实现原理、dealloc内释放实例变量、便利构造器方法的实现原理、collection的内存管理

    代码: // // main.m #import <Foundation/Foundation.h> #import "Person.h" #import " ...

  8. Objective-C 类属性和方法的訪问权限

    OC中提供了4种訪问权限.@private, @public, @protected这三种和其它的C++, Java是一样的,@package这个訪问权限并非Java里的包訪问权限,OC中没有包的概念 ...

  9. python面向对象中类对象、实例对象、类变量、实例变量、类方法、实例方法、静态方法

    1. 类对象和实例对象 Python中一切皆对象,Python类本身也是一种对象,类定义完成后,会在当前作用域中定义一个以类名为名字的命名空间.类对象具有以下两种操作: 可以通过“类名()”的方式实例 ...

随机推荐

  1. jquery ajax 同步异步的执行

    jquery ajax 同步异步的执行   大家先看一段简单的jquery ajax 返回值的js 代码 function getReturnAjax{  $.ajax({    type:" ...

  2. 「 HDU 1978 」 How many ways

    # 解题思路 记忆化搜索 一个点可以跳到的点,取决于它现在的能量.而且有一个显而易见的性质就是一条可行路径的终点和起点的横坐标之差加上纵坐标之差肯定小于等于起点的能量. 因为跳到一个点之后,能量和之前 ...

  3. [Algorithm] 2. Trailing Zeros

    Description Write an algorithm which computes the number of trailing zeros in n factorial. Example 1 ...

  4. PDO、PDOStatement、PDOException

    最近在学PDO  比较详细的资料 出处:http://blog.csdn.net/hsst027/article/details/23682003 PDO中包含三个预定义的类,它们分别是PDO.PDO ...

  5. OpenCV+Python实现视频文件裁剪功能

    Python编程实现对视频文件进行剪切的功能.截取指定长度的视频并保存,运行后首先选择要裁剪的视频,然后输入开始时间点和停止时间点即可.将剪切后的视频保存为output.avi文件 所属网站分类: 资 ...

  6. windows窗口过程函数名词解析

    windows窗口过程函数名词解析 LRESULT CALLBACK WndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam) 1. LR ...

  7. UVa 1600 Patrol Robot(BFS)

    题意: 给定一个n*m的图, 有一个机器人需要从左上角(1,1)到右下角(n,m), 网格中一些格子是空地, 一些格子是障碍, 机器人每次能走4个方向, 但不能连续穿越k(0<= k <= ...

  8. js在HTML中的三种写法

    1.内联样式 内联样式分为两种,一是直接写入元素的标签内部 <html> <title>js样式内联写法</title> <meta http-equiv=& ...

  9. 编程数学-∑(求和符号)-Sigma

    百度百科:∑ 在数学中,我们把它作为求和符号使用. 大写Σ用于数学上的总和符号,比如:∑Pi,其中i=1,2,...,T,即为求P1 + P2 + ... + PT的和.小写σ用于统计学上的标准差.西 ...

  10. MySQL prepare语句的SQL语法

    MySQL prepare语法: PREPARE statement_name FROM preparable_SQL_statement; /*定义*/ EXECUTE statement_name ...