《Effective Objective-C 2.0:编写高质量iOS与OS X代码的52个有效方法》 笔记3 为什么要用属性
2016-04-15 14:13
821 查看
在描述个人信息的类中,也许会存放人名、生日、地址等内容。可以在类接口的public区段中声明一些实例变量:
原来编过Java或C++程序的人应该比较熟悉这种写法,在这些语言中,可以定义实例变量的作用域。然而编写Objective-C代码时却很少这么做。这种写法的问题是:对象布局在编译期(compile time)就已经固定了。只要碰到访问_firstName变量的代码,编译器就把其替换为“偏移量”(offset),这个偏移量是“硬编码”(hardcode),表示该变量距离存放对象的内存区域的起始地址有多远。这样做目前来看没问题,但是如果又加了一个实例变量,那就麻烦了。比如说,假设在_firstName之前又多了一个实例变量:
原来表示_firstName的偏移量现在却指向_dateOfBirth了。把偏移量硬编码于其中的那些代码都会读取到错误的值。图2-1演示了此情况,请对比在类中加入_dateOfBirth这一实例变量之前与之后的内存布局,其中假设指针为4个字节。
如果代码使用了编译期计算出来的偏移量,那么在修改类定义之后必须重新编译,否则就会出错。例如,某个代码库中的代码使用了一份旧的类定义。如果和其相链接的代码使用了新的类定义,那么运行时就会出现不兼容现象(incompatibility)。各种编程语言都有应对此问题的办法。Objective-C的做法是,把实例变量当做一种存储偏移量所用的“特殊变量”(special variable),交由“类对象”(class object)保管(第14条详述了类对象)。偏移量会在运行期查找,如果类的定义变了,那么存储的偏移量也就变了,这样的话,无论何时访问实例变量,总能使用正确的偏移量。甚至可以在运行期向类中新增实例变量,这就是稳固的“应用程序二进制接口”(Application
Binary Interface,ABI)。ABI定义了许多内容,其中一项就是生成代码时所应遵循的规范。有了这种“稳固的”(nonfragile)的ABI,我们就可以在“class-continuation分类”或实现文件中定义实例变量了。所以说,不一定要在接口中把全部实例变量都声明好,可以将某些变量从接口的public区段里移走,以便保护与类实现有关的内部信息。
这个问题还有一种解决办法,就是尽量不要直接访问实例变量,而应该通过存取方法来做。虽说属性最终还是得通过实例变量来实现,但它却提供了一种简洁的抽象机制。你可以自己编写存取方法,然而在正规的Objective-C编码风格中,存取方法有着严格的命名规范。正因为有了这种严格的命名规范,所以Objective-C这门语言才能根据名称自动创建出存取方法。这时@property语法就派上用场了。
在对象接口的定义中,可以使用属性,这是一种标准的写法,能够访问封装在对象里的数据。因此,也可以把属性当做一种简称,其意思是说:编译器会自动写出一套存取方法,用以访问给定类型中具有给定名称的变量。例如下面这个类:
对于该类的使用者来说,上述代码写出来的类与下面这种写法等效:
然而属性还有更多优势。如果使用了属性的话,那么编译器就会自动编写访问这些属性所需的方法,此过程叫做“自动合成”(autosynthesis)。需要强调的是,这个过程由编译器在编译期执行,所以编辑器里看不到这些“合成方法”(synthesized method)的源代码。除了生成方法代码之外,编译器还要自动向类中添加适当类型的实例变量,并且在属性名前面加下划线,以此作为实例变量的名字。在前例中,会生成两个实例变量,其名称分别为_firstName与_lastName。也可以在类的实现代码里通过@synthesize语法来指定实例变量的名字:
@interface EOCPerson : NSObject { @public NSString *_firstName; NSString *_lastName; @private NSString *_someInternalData; } @end
原来编过Java或C++程序的人应该比较熟悉这种写法,在这些语言中,可以定义实例变量的作用域。然而编写Objective-C代码时却很少这么做。这种写法的问题是:对象布局在编译期(compile time)就已经固定了。只要碰到访问_firstName变量的代码,编译器就把其替换为“偏移量”(offset),这个偏移量是“硬编码”(hardcode),表示该变量距离存放对象的内存区域的起始地址有多远。这样做目前来看没问题,但是如果又加了一个实例变量,那就麻烦了。比如说,假设在_firstName之前又多了一个实例变量:
@interface EOCPerson : NSObject { @public NSDate *_dateOfBirth; NSString *_firstName; NSString *_lastName; @private NSString *_someInternalData; } @end
原来表示_firstName的偏移量现在却指向_dateOfBirth了。把偏移量硬编码于其中的那些代码都会读取到错误的值。图2-1演示了此情况,请对比在类中加入_dateOfBirth这一实例变量之前与之后的内存布局,其中假设指针为4个字节。
如果代码使用了编译期计算出来的偏移量,那么在修改类定义之后必须重新编译,否则就会出错。例如,某个代码库中的代码使用了一份旧的类定义。如果和其相链接的代码使用了新的类定义,那么运行时就会出现不兼容现象(incompatibility)。各种编程语言都有应对此问题的办法。Objective-C的做法是,把实例变量当做一种存储偏移量所用的“特殊变量”(special variable),交由“类对象”(class object)保管(第14条详述了类对象)。偏移量会在运行期查找,如果类的定义变了,那么存储的偏移量也就变了,这样的话,无论何时访问实例变量,总能使用正确的偏移量。甚至可以在运行期向类中新增实例变量,这就是稳固的“应用程序二进制接口”(Application
Binary Interface,ABI)。ABI定义了许多内容,其中一项就是生成代码时所应遵循的规范。有了这种“稳固的”(nonfragile)的ABI,我们就可以在“class-continuation分类”或实现文件中定义实例变量了。所以说,不一定要在接口中把全部实例变量都声明好,可以将某些变量从接口的public区段里移走,以便保护与类实现有关的内部信息。
这个问题还有一种解决办法,就是尽量不要直接访问实例变量,而应该通过存取方法来做。虽说属性最终还是得通过实例变量来实现,但它却提供了一种简洁的抽象机制。你可以自己编写存取方法,然而在正规的Objective-C编码风格中,存取方法有着严格的命名规范。正因为有了这种严格的命名规范,所以Objective-C这门语言才能根据名称自动创建出存取方法。这时@property语法就派上用场了。
在对象接口的定义中,可以使用属性,这是一种标准的写法,能够访问封装在对象里的数据。因此,也可以把属性当做一种简称,其意思是说:编译器会自动写出一套存取方法,用以访问给定类型中具有给定名称的变量。例如下面这个类:
@interface EOCPerson : NSObject @property NSString *firstName; @property NSString *lastName; @end
对于该类的使用者来说,上述代码写出来的类与下面这种写法等效:
@interface EOCPerson : NSObject - (NSString*)firstName; - (void)setFirstName:(NSString*)firstName; - (NSString*)lastName; - (void)setLastName:(NSString*)lastName; @end
然而属性还有更多优势。如果使用了属性的话,那么编译器就会自动编写访问这些属性所需的方法,此过程叫做“自动合成”(autosynthesis)。需要强调的是,这个过程由编译器在编译期执行,所以编辑器里看不到这些“合成方法”(synthesized method)的源代码。除了生成方法代码之外,编译器还要自动向类中添加适当类型的实例变量,并且在属性名前面加下划线,以此作为实例变量的名字。在前例中,会生成两个实例变量,其名称分别为_firstName与_lastName。也可以在类的实现代码里通过@synthesize语法来指定实例变量的名字:
@implementation EOCPerson @synthesize firstName = _myFirstName; @synthesize lastName = _myLastName; @end
相关文章推荐
- Swift与Objective-C API的交互
- Object-C 属性
- hello objective-c
- 深入理解Objective-C:方法缓存
- Objective-C之NSNotification Center(通知中心)
- 深入理解Objective-C:Category
- 使用Object.defineProperty实现简单的js双向绑定
- 目标检测“A MultiPath Network for Object Detection”
- iOS Objecttive-C语言 定义常量的几种方式与区别 static const 定义常量 少用#define预处理指令 多用类型常量
- MRC 和 ARC的区别
- object类的9个方法
- 媒体播放器探索之IMediaObject
- 使用Object.defineProperty实现简单的js双向绑定
- Effective Object C 2.0——熟悉Object-C
- Android ObjectAnimator 无限循环播放,实现上下左右浮动效果
- Object常用方法介绍及注意事项equals(),hashCode(),getClass(),toString()
- From C++ to Objective-C
- Java中的Object2
- 09.---抽象类、接口、instanceof、final、Object
- Objective-C之KVO(键值监听)