OC基础之理解属性

·  阅读 969

property的概念

@Property是声明属性的语法,作为 Objective-C 的一项特性,主要的作用就在于封装对象中的数据。它可以快速方便的创建实例变量,并为实例变量创建存取器,并允许我们通过点语法使用存取器。

property的本质

property到底是什么呢?实际上@property = 实例变量 + get方法 + set方法

@property = ivar + getter + setter;
复制代码

所以,现在我们写@property声明属性,其实是做了三件事
.h: 声明了getter和setter方法;
.m: 声明了实例变量(默认:下划线+属性名);
.m: 实现了getter和setter方法。

自动合成

定义一个@property,在编译期间,编译器会生成实例变量、getter方法、setter方法,这些方法、变量是通过自动合成(autosynthesize)的方式生成并添加到类中。实际上,一个类经过编译后,会生成变量列表ivar_list,方法列表method_list,每添加一个属性,在变量列表ivar_list会添加对应的变量,如_name,方法列表method_list中会添加对应的setter方法和getter方法。实际上,自动合成对应的代码是:

@synthesize name = _name;
复制代码

@synthesize 合成实例变量的规则
1.如果指定了成员变量的名称,会生成一个指定的名称的成员变量
2.如果这个成员已经存在了就不再生成了.
3.如果实现了@synthesize,但没有指定成员变量的名称,会自动生成一个与属性同名的成员变量

动态合成

既然有自动合成,那么相对应的就要有非自动合成,非自动合成又称为动态合成。如果我们想要动态合成,需要自己写如下代码:

@dynamic name;
复制代码

这样代码就告诉编译器,name属性的变量名、getter方法、setter方法由开发者自己来添加,编译器无需处理。

@synthesize和@dynamic的使用场景

  • @synthesize使用场景

回答这个问题前,我们要搞清楚一个问题,什么情况下不会自动合成成员变量?

1)同时重写了 setter 和 getter 时
2)重写了只读属性的 getter 时
3)使用了 @dynamic 时
4)在 @protocol 中定义的所有属性
5)在 category 中定义的所有属性
6)重载的属性

以上情况编译器为什么不自动合成成员变量
前3条可以总结为:当我们尝试通过实现 @property 的所有“存取方法”或者明确使用dynamic来达到此目的时,编译器会认为我们打算手动管理 @property,于是编译器就禁用了 autosynthesis(自动合成)。
protocol中不自动合成的原因:
protocol中,没有存放实例变量的内存空间
category中不自动合成的原因:
category中,类的内存空间已经在编译器确定,无法给类添加实例变量
重载的属性不自动合成的原因:
重载属性时,父类已经生成该属性的实例变量,子类无法通过自动合成来生成相同的实例变量,也无法访问父类的实例变量。所以如果需要创建实例变量,你必须 使用 @synthesize 来手动合成ivar。

  • @dynamic的使用场景

1)明确标记不希望自动合成实例变量,getter方法、setter方法()
2)子类复写父类属性时,在子类使用@dynamic,可明确告诉使用父类方法
3)runtime中动态添加属性方法

property特性

@property (copy, nonatomic) NSString *name;
复制代码

这种写法,大家肯定都写过,不过,后面跟着的这个括号又是什么玩意儿呢?

官方把括号里面的东西,叫做「attribute/特性」。当我们把括号里的特性都删掉,发现还能正常工作。因为如果什么都不写,系统就会取用默认值。
关于ARC下,不显示指定属性关键字时,属性的默认特性如下:
1.基本数据类型:atomic readwrite assign
2.普通OC对象: atomic readwrite strong

属性拥有的特性可以分为以下几类:

Atomicity(原子性)

  • atomic(默认):在一定程度上可以保证线程安全,「atomic的作用只是给getter和setter加了个锁」。也就是说,有线程在访问setter,其他线程只能等待完成后才能访问,能够保证同一时间只有一个线程执行写入操作。同一个时间多个线程都可以取值),atomic会让你得到一个有意义的值(valid value),但是不能保证你获得的是哪个值(有可能是被其他线程修改过的值,也有可能是没有修改过的值),影响效率,使用少。

  • nonatomic:非原子的,线程不安全的,效率高,使用广泛。

保证线程安全可以详细见多线程相关文章,后续也会更新相关文章

Access(存取特性)

存取特性有readwrite(默认值)和readonly,如果是readonly,只生成getter。

Storage(内存管理特性)

最常用到strong、weak、assign、copy4个attributes。(retain在ARC中使用很少,就不介绍了)

  • strong:表明你需要引用(持有)这个对象(reference to the object),负责保持这个对象的生命周期。
  • weak:也会引用(reference/pointer),指向对象。但是不会增加引用计数。如果对象A被销毁,所有指向对象A的弱引用都会自动设置为nil。常用weak解决循环引用问题。
  • assign:它的作用和weak类似,唯一区别是:如果对象A被销毁,所有指向这个对象A的assign属性并不会自动设置为nil。这时候这些属性就变成野指针,再访问这些属性,程序就会crash。因此,assign就变成用于修饰基本数据类型
  • copy:与 strong 类似。但在setter方法中,不保留新值,而是将其拷贝,并对传入的对象进行引用计数加1的操作。通常情况下,不可变对象属性修饰符使用copy,可变对象属性修饰符使用strong。

扩展

  • 方法名,getter=< name > 和 setter=< name >。按字面意思,很容易理解,就是重命名getter和setter方法。
  • Nullability,为了更好地和Swift混编(配合Swift的optional类型),常用的情况主要是以下2类
    • nullable:对象「可为空」
    • nonnull:对象「不可为空」

其他相关

浅拷贝和深拷贝有什么区别?

  • 浅拷贝:对内存地址的复制,让目标对象指针和源对象指向同一块内存空间

浅拷贝特点:
1.浅拷贝会增加被拷贝对象的引用计数
2.没有发生新的内存分配

  • 深拷贝: 让目标对象指针和源对象指针指向两片内容相同的内存空间

深拷贝的特点: 1.不会增加被拷贝对象的引用计数,因为没有新指针指向原来的内存空间
2.产生了新的内存分配,出现了两块内存

深拷贝和浅拷贝的区别:
1.是否开辟了新的内存空间,深拷贝开辟了新的内存空间,浅拷贝没有开辟
2.是否影响了引用计数,深拷贝没有影响,浅拷贝增加了引用计

NSMutableArray数组A,用过mutableCopy拷贝生成的数组B,虽然A、B数组内存地址不一致,但是其元素指向的内存地址仍然是一样的
复制代码

关于可变对象和不可变对象,使用copy和mutableCopy结果如下:

自定义对象如何支持copy方法

自定义对象可以支持copy方法,我们所需要做的是:自定义对象遵守NSCopying协议,且实现copyWithZone方法。NSCopying协议是系统提供的,直接使用即可。

分类:
iOS
标签: