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

这是 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. cmanformat - 不是命令啦,是个演示文件

    描述 DESCRIPTION cmanformat 是 man pages 格式的演示文件. 由于系统不同会有差异.在 XWindow 下会好些. __________________________ ...

  2. Zend Studio 服务器根目录设置

    在 Apache 服务器根目录里查找 \conf\httpd.conf 例如:C:\AppServ\Apache24\conf\httpd.conf 打开后查找 DocumentRoot 标记 修改调 ...

  3. CAD控件使用教程 自定义实体的实现

    自定义实体的实现 1 .       自定义实体... 3 1.1      说明... 3 1.2      类的类型信息... 3 1.3      worldDraw.. 4 1.4      ...

  4. BZOJ1079: [SCOI2008]着色方案 (记忆化搜索)

    题意:有n个木块排成一行,从左到右依次编号为1~n.你有k种颜色的油漆,其中第i种颜色的油漆足够涂ci个木块. 所有油漆刚好足够涂满所有木块,即c1+c2+...+ck=n.相邻两个木块涂相同色显得很 ...

  5. Spring对象类型——单例和多例

    由于看淘淘商城的项目,涉及到了项目中处理spring中bean对象的两种类型,分别是单例和多例,就在此记录一下,方便加深理解,写出更加健壮的代码. 一.单例和多例的概述 在Spring中,bean可以 ...

  6. Linux命令整理(2018/9/9-2018/9/15)

    根据本周的Linux学习进度,整理了部分Linux知识及常用命令,待完善…… 1.显示默认启动方式(默认启动目标): systemctl get-default 2.设置默认启动方式(默认启动目标): ...

  7. 从yii2框架中的di容器源码中了解反射的作用

    反射简介 参考官方简介的话,PHP 5 具有完整的反射 API,添加了对类.接口.函数.方法和扩展进行反向工程的能力. 此外,反射 API 提供了方法来取出函数.类和方法中的文档注释. YII2框架中 ...

  8. 基于nvm的Node、NPM的版本管理(NPM permission error的解决)

    最近在使用npm过程中,发现全局安装总会遇到permission相关的错误,所以总是要在前面加sudo,还得不停输入密码. 懒惰使我进步,随手google了一下相关问题的解决方案,发现npm在官方文档 ...

  9. 用element-ui的走马灯carousel轻松实现自适应全屏banner图

    写在前面:网站轮播图建议使用swiper组件,非常方便快捷.vue轮播图插件之vue-awesome-swiper 接手一个项目,轮播图是用element-ui的carousel实现的,看起来效果还不 ...

  10. 51中xdata,idata,data,pdata的区别

    51系列中data,idata,xdata,pdata的区别 data: 固定指前面0x00-0x7f的128个RAM,可以用acc直接读写的,速度最快,生成的代码也最小. idata: 固定指前面0 ...