在对象内部尽量直接访问实例变量 --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方法中,总是应该通过实例变量来读写数据
  • 有时会使用懒加载技术配置某些数据,这种情况下,需要通过属性来读取数据.
时间: 2024-08-02 09:23:23

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

第7条:在对象内部尽量直接访问实例变量

在对象外部:  总是使用属性来访问. 在对象内部: 除了几种特殊情况外,强烈建议在读取实例变量时采用直接访问形式.而在设置实例变量时通过属性来做. 在初始化方法及dealloc方法中,总是使用直接通过实例变量来读写数据. 例:以下会抛出异常 -(void)setLastName:(NSString*)lastName{ self.lastName = lastName; } 在"惰性初始化"情况下,必须通过“获取方法”来访问属性,否则,实例变量永远不会初始化. 例: 以下是错误演示 -

属性存取、直接访问实例变量

属性的读取采用点语法,访问对应的set和get方法.而直接访问是直接访问的对象实例的内存.这两者有什么区别?在什么情况应该使用哪种方法呢? 一.区别 直接访问实例变量有如下几种特质. 1.不经过Objective-C的方法派发,直接访问实例变量的内存,速度快. 2.由于没调用set方法,所以绕过了属性定义时声明的"内存管理语义",只会保留新值,释放旧值. 3.不会触发"键值观测"KVO机制. 4.无法通过给set和get打断点来进行调试. 二.使用场景 通用情况:

KVC-键值编码是一种间接地访问实例变量的方式

KVC 键值编码是一种间接的访问实例变量的方式[对象 setValue:@"A" forKey:@"name"]; 工作原理:    1.先查找该类有没有对应的setName: setter方法.如果有则调用setter方法给实例变量赋值,如果没有,转到第二步:    2.在查找带下划线的实例变量_name.如果有则给_name赋值,如果没有则转到第三步    3.最后查找和key值相同的实例变量name,如果有,则给name赋值,如果没有,转到第四步    4.调

访问属性和访问实例变量的区别

1.由于不经过Objective-C的“方法派发”(method dispatch)步骤,所以直接访问实例变量的速度会比较快.在这种情况下,编译器所生成的代码会直接访问保存对象实例的那块内存: 2.直接访问实例变量时,不会调用其“设置方法”,这就绕过了为设置相关属性所定义的“内存管理语义”: 3.如果直接访问实例变量,那么不会触发“键值观察”(key-Value Observing,KVO)通知.这样做是否会产生问题,还取决于具体的对象行为. 因此合理的折中方案是:在写入实例变量时,通过其“设置

李洪强iOS开发之创建一个对象并访问实例变量

// //  main.m //  07 - 创建一个对象并且访问实例变量 // //  Created by vic fan on 16/7/3. //  Copyright © 2016年 李洪强. All rights reserved. // // Car *car = [Car new]; 类名 *指针 = [类名 new]; [Car new];做了三件事: 1 向计算机申请内存空间 2 初始化类中的实例变量 3 返回对象的内存地址 #import <Foundation/Found

对象的行为——方法操作实例变量

对象有:[状态] 和 [行为] 两种属性:分别由[实例变量]和[方法]类表示. 类所描述的是[对象知道什么]:对象所知者 就是实例变量 [对象执行什么]:对象所为者 就是方法 类的每个实例(也就是特定类型 的 每个对象 ),可以维持自己的实例变量. 状态影响行为<===>行为影响状态: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 调用参数的时候 实参的传递:形参的接收: //Java是通过值传递的,也就是通过拷贝传递(不论传递的是对象还是主数据类型的变量), //方

类,对象与实例变量

类就是一类事物,而对象就是一个事物,一类事物中包含若干的事物(而这其中的具体的事物就是对象),比如说:车,但没法具体到哪辆车,这就是一个类的概念,而如果说,我家的那辆轿车,就可以想到那具体的一辆车,而这个具体的车就是对象的概念. 类变量:比较特殊的实例变量,用static修饰的,一个类共用这个变量,所有由这类生成的对象都共用这个变量,类装载时就分配存储空间.一个对象修改了变量,则所以对象中这个变量的值都会发生改变. 实例变量:从属于类,由类生成对象时,才分配存储空间,各对象间的实例变量互不干扰,

Oc(实例变量可见度与方法)(InstanceVariableAndMethod)

main函数 // //  main.m //  2-3 LessionInstanceVariableAndMethod // //  Created by lanouhn on 15/2/3. //  Copyright (c) 2015年 lanouhn. All rights reserved. // #import <Foundation/Foundation.h> #import "Boy.h" #import "MobilePhone.h"

ruby中实例变量、类变量等等的区别和联系

ruby的变量有局部变量,全局变量,实例变量,类变量,常量. 1.局部变量 局部变量以一个小写字母开头或下划线开头 局部变量有局部作用域限制(比如一个block内),它的作用域起始于声明处,结束于该声明所在的块.方法定义.类/模块定义的结尾.大家在写代码时经常这样写: irb(main):001:0> i=123    ---------- 这里的i就是局部变量 =>123 irb(main):002:0> s="hi"   ---------- 这里的s就是局部变量