Effective Object-C之属性

序言

用Objective-C编程时,通过对象来存储并传递数据,在对象之间传递数据并执行任务的过程叫做“消息传递”。

理解“属性”

“属性”用于封装对象中的数据。OC对象通常把需要的数据保存为各种实例变量。实例变量一般通过“存取方法”来访问。而目前开发者可以令编译器自动编写与属性相关的存取方法。此特性引入了一种新的“点语法”,使开发者可以更为容易地依照类对象来访问存放于其中的数据。
哪些问题可以用属性来解决? 其中所体现的关键特性?
在描述个人信息的类中,会存放人名、生日、地址等内容。可以在类接口的public区段中声明一些实例变量:

@interface EOCPerson: NSObject {
@public
    NSString *_firstName;
    NSString *_lastName;
@private
    NSString *_someInstance;
}
@end

对于上面的代码,对象布局在编译期就已经固定了,只要碰到_firstName变量的代码,编译器就把其替换为“偏移量(offset)”,这个偏移量是“硬编码”,表示该变量距离存放对象的内存区域的起始地址有多远。
这样做目前是没什么问题,但是如果又加了一个实例变量,那就麻烦了,比如说在_firstName之前多加一个实例变量_dataOfBirth。原来表示_firstName的偏移量现在却指向了_dataOfBirth了。如图:在类中新增另一个实例变量前后的数据布局图
这里写图片描述
如果代码使用了编译期计算出来的偏移量,那么在修改类定义之后必须重新编译,否则就会出错。
例如,某个代码库中使用了一份旧的类定义。如果和其相链接的代码使用了新的类定义,那么运行时就会出现不兼容现象。OC的做法是,把实例变量当做一种存储偏移量所用的“特殊变量”,交由“类对象”保管。偏移量会在运行期查找,如果类的定义变了,那么存储的偏移量也就变了,这样的话,无论何时访问实例变量,总能使用正确的偏移量。甚至可以在运行期向类中新增实例变量,这就是稳固的“应用程序二进制接口(ABI)”。ABI其中一项就是生成代码时所应遵循的规范。有了ABI,我们就可以在“class-continuation分类”或实现文件中定义实例变量了。所以说,不一定要在接口中把全部实例变量都声明好,可以将某些变量从接口的public区段里移走,以便保护与类实现有关的内部信息。
*编译器会自动写出一套存取方法,用以访问给定类型中具有给定名称的变量* ——@property
@synthesize语法来制定实例变量的名字**若不想令编译器自动合成存取方法,则可以自己实现。就是使用@dynamic关键字,它告诉编译器:不要自动创建实现属性所用的实例变量,也不要创建存取方法。

属性特质

使用属性时有个问题要注意,就是各种特质设定也会影响编译器所生成的存取方法,比如下面这个属性就指定三项特质:
@property (nonatomic , readwrite , copy) NSString *firstName;

原子性

atomic :原子的,使用同步锁,开销较大。具体就是对setter,getter方法加锁,但并不能保证线程安全,比如在为某个属性设置值时,有多个线程读取getter方法,其中一个把未修改好的属性值读取出来,另一个读取的是修改好的属性值,会造成读取的值不一致。平时一般用nonatomic

读、写权限

——readwrite(读写)特质的属性拥有”getter和setter“,若该属性由@synthesize实现,则编译器会自动生成这两个方法。
——readonly(只读)特质的属性仅拥有获取方法,只有当该属性由@synthsize实现时,编译器才会为其合成获取方法。

内存管理语义

属性用于封装数据,而数据则要有”具体的所有权语义“。例如用”设置方法“设定一个新值时,它是应该”保留“此值呢?还是只将其赋给底层实例变量就好?编译器在合成存取方法时,要根据此特质来决定所生成的代码。
——assign 针对基本数据类型的简单赋值操作
——strong 表明了该属性定义了一种“拥有关系”。为这种属性设置新值时,该方法会先保留新值,并释放旧值,然后再将新值设置上去。
——weak 此特质表明该属性定义了一种”非拥有关系”。为这种属性设置新值时,该方法既不保留新值,也不释放旧值。和assign区别在于属性所指的对象遭到摧毁时,属性值会自动置为nil。
—— unsafe_unretained 此特质语义和assign相同,但是它适用于“对象类型”,该特质表达一种“非拥有关系”,当目标对象遭到摧毁时,属性值不会自动清空,这一点和weak区别
——copy 此特质所表达的所属关系与strong类似。然而设置方法并不保留新值,而是将其“拷贝”。当属性类型为NSString *时,经常用此特质来保护其封装性,因为传递给设置方法的新值有可能指向一个NSMutableString类型的实例。这个类时NSString的子类,表示以后总可以修改其值的字符串,此时若是不拷贝字符串,那么设置属性之后,字符串的值就会在对象不知情的情况下遭人更改。所以这是要拷贝一份“不可变”的字符串,确保对象中的字符串值不会无意间变动。只要实现属性所用的对象是“可变的”,就应该在设置新属性值时拷贝一份。

在对象内部尽量直接访问实例变量

问题:什么时候通过属性来访问实例变量还是直接访问?
在对象内部读取数据时,应该直接通过实例变量来读,而写入数据时,则应通过属性来写。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值