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

这是 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方法中,总是应该通过实例变量来读写数据

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

在对象内部尽量直接訪问实例变量 --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. ztree 样式修改 white-space: nowrap; 后 下划线要是跟上的话 宽度 width 要 auto 就自动更新了

    width:auto; border-bottom:1px solid #ccc; height:30px; display: inline-block;white-space: nowrap;

  2. listener.log文件过大导致oracle假死

    /home/u01/oracle/product/11gr2/db_1/log/diag/tnslsnr/VM_179_95_centos/listener/trace/listener.log li ...

  3. idea vue提示

    我在idea中,装了,vue.js插件,可就是死活,没有提示,我的html 文件是 .html扩展名,我想是不是因为这个原因,我就新建了个,test.vue,哈哈,提示出来了,我又在其它.html文件 ...

  4. sql备份

    SELECT id,Name FROM TeachSite GROUP BY id select * from #temp as [type], SchoolRollID,SUM(Chargeable ...

  5. super(Student,self).__init__()初始化的是什么东西?

    继承不是为了继承里面原来的属性和值么,不初始化的话,会有什么问题? 2015-04-04源自:python进阶 5-17642 浏览2 回答 最佳回答 2015-05-05 1 super(Stude ...

  6. react-native IOS Build input file cannot be found: '~~~~~/node_modules/react-native/Libraries/WebSocket/libfishhook.a'

    尝试添加或者删除libfishhook.a 如果解决了你的问题请点个赞!

  7. 全国高校绿色计算大赛 预赛第三阶段(Python)(随机数)

    只提交了随机数 (真心不会 T-T ) import csv import random import pandas as pd import numpy as np # 预测结果文件:src/ste ...

  8. 大项目之网上书城(七)——书页面以及加入购物车Servlet

    目录 大项目之网上书城(七)--书页面以及加入购物车Servlet 主要改动 1.shu.jsp 代码 效果图 2.shu.js 代码 3.index.jsp 代码 效果图 4.FindBookByC ...

  9. 对django中间件的理解

    1. 什么是中间件(Django)? 对Django而言,中间件就是继承自MiddlewareMixin(位于django.utils.deprecation模块下)的类,该类对请求(request) ...

  10. Android TextView内容过长加省略号

    在Android TextView中有个内容过长加省略号的属性,即ellipsize,用法如下: 在xml中: android:ellipsize = "end" //省略号在结尾 ...