iOS 深入理解 @property
Posted Xiejunyi12
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了iOS 深入理解 @property相关的知识,希望对你有一定的参考价值。
@property 的本质是什么?
@property = ivar + getter + setter;
“属性” (property)作为 Objective-C 的一项特性,主要的作用就在于封装对象中的数据。 Objective-C 对象通常会把其所需要的数据保存为各种实例变量。实例变量一般通过“存取方法”(access method)来访问。其中,“获取方法” (getter)用于读取变量值,而“设置方法” (setter)用于写入变量值。这个概念已经定型,并且经由“属性”这一特性而成为 Objective-C 2.0 的一部分。 而在正规的 Objective-C 编码风格中,存取方法有着严格的命名规范。 正因为有了这种严格的命名规范,所以 Objective-C 这门语言才能根据名称自动创建出存取方法。其实也可以把属性当做一种关键字,其表示:
编译器会自动写出一套存取方法,用以访问给定类型中具有给定名称的变量。 所以你也可以这么说:@property = getter + setter;
自动合成
when?
- 编译期
@synthesize
- @synthesize语法来指定实例变量的名字.
@implementation Person
@synthesize firstName = _myFirstName;
@synthesize lastName = _myLastName;
@end
反编译做了什么
- OBJC_IVAR_ 类名 属性名称 :该属性的“偏移量” (offset),这个偏移量是“硬编码” (hardcode),表示该变量距离存放对象的内存区域的起始地址有多远。
- setter 与 getter 方法对应的实现函数
- ivar_list :成员变量列表
- method_list :方法列表
- prop_list :属性列表
也就是说我们每次在增加一个属性,系统都会在 ivar_list 中添加一个成员变量的描述,在 method_list 中增加 setter 与 getter 方法的描述,在属性列表中增加一个属性的描述,然后计算该属性在对象中的偏移量,然后给出 setter 与 getter 方法对应的实现,在 setter 方法中从偏移量的位置开始赋值,在 getter 方法中从偏移量开始取值,为了能够读取正确字节数,系统对象偏移量的指针类型进行了类型强转.
默认情况
- 对应基本数据类型默认关键字是 atomic,readwrite,assign
- 对于普通的 Objective-C 对象 atomic,readwrite,strong
@property
- 原子性— nonatomic 特质
- 读/写权限—readwrite(读写)、readonly (只读)
- 内存管理语义—assign、strong、 weak、unsafe_unretained、copy
- 方法名—getter= 、setter=
eg:@property (nonatomic, getter=isAllow) BOOL allow;
使用atomic一定是线程安全的吗?
不是的
- atomic原子操作,系统会为setter方法加锁。(但是也不安全,需要更深层次的锁定)
- nonatomic不会为setter方法加锁。
- atomic:非线程安全,还是需要消耗大量系统资源来为属性加锁
- nonatomic:非线程安全,适合内存较小的移动设备
引用计数内存管理的思考方式:
- 自己生成的对象,自己所持有。
- 非自己生成的对象,自己也能持有。
- 自己持有的对象不再需要时释放。
- 非自己持有的对象无法释放。
strong
在ARC下,实例变量本身是强引用,当ARC将传入值赋给实例变量时,它会保留传入的值,释放现有
实例变量的值。非ARC下,setter方法会保留传入的值和释放现有实例变量的值。strong,retain
是同义词。
copy
如果是不可变的值,行为与strong相同。
如果是可变的值,会将一个副本赋给实例变量。当一个不可变类有一个可变的子类时
(NSString NSMutableString,NSArray NSMutableArray)可以防止setter 方法传递一个
可变的子类的对象。会导致我们在不知情的情况下修改对象的值。
weak
- 带__weak 修饰符的变量不持有对象,所以在超出其变量作用域时,对象被释放。
可以这样理解,__weak修饰的变量没有对象的所有权,不增加对象的引用计数。
什么情况使用 weak 关键字?
在 ARC 中,在有可能出现循环引用的时候,往往要通过让其中一端使用 weak 来解决,
比如: delegate 代理属性自身已经对它进行一次强引用,没有必要再强引用一次,此时也会使用 weak,
自定义 IBOutlet 控件属性一般也使用 weak;当然,也可以使用strong。
assign
- 适用于值类型。而weak必须用于对象。
assign适用于基本数据类型,weak是适用于NSObject对象,并且是一个弱引用。
assign其实也可以用来修饰对象,那么我们为什么不用它呢?因为被assign修饰的对象在释放之后,指针的地址还是存在的,也就是说指针并没有被置为nil。如果在后续的内存分配中,刚好分到了这块地址,程序就会崩溃掉。
而weak修饰的对象在释放之后,指针地址会被置为nil。所以现在一般弱引用就是用weak。
@synthesize和@dynamic
@property有两个对应的词,一个是 @synthesize,一个是 @dynamic。如果 @synthesize和 @dynamic都没写,那么默认的就是@syntheszie var = _var;
@synthesize 的语义是如果你没有手动实现 setter 方法和 getter 方法,那么编译器会自动为你加上这两个方法。
@dynamic 告诉编译器:属性的 setter 与 getter 方法由用户自己实现,不自动生成。(当然对于 readonly 的属性只需提供 getter 即可)。假如一个属性被声明为 @dynamic var,然后你没有提供 @setter方法和 @getter 方法,编译的时候没问题,但是当程序运行到 instance.var = someVar,由于缺 setter 方法会导致程序崩溃;或者当运行到 someVar = var 时,由于缺 getter 方法同样会导致崩溃。编译时没问题,运行时才执行相应的方法,这就是所谓的动态绑定。
@synthesize 合成实例变量的规则,有以下几点:
- 如果指定了成员变量的名称,会生成一个指定的名称的成员变量,
- 如果这个成员已经存在了就不再生成了.
- 如果是 @synthesize xjy; 还会生成一个名称为xjy的成员变量,也就是说:如果没有指定成员变量的名称会自动生成一个属性同名的成员变量,
- 如果是 @synthesize xjy = _xjy; 就不会生成成员变量了.
什么时候不会自动合成
- 同时重写了 setter 和 getter 时
- 重写了只读属性的 getter 时
- 使用了 @dynamic 时
- 在@protocol中定义的所有属性
- 在category中定义的所有属性
- 重载的属性 (子类中重载父类的属性)
@interface MyLabel ()
@property (nonatomic, strong) NSString *text;
@end
@implementation MyLabel
//必须用synthesize 手动指定ivar的名字就可以避免重载
@synthesize text = _myText;
@end
我的博客 转载注明出处
https://junyixie.github.io/2016/12/28/%E8%AF%A6%E8%A7%A3-property/
以上是关于iOS 深入理解 @property的主要内容,如果未能解决你的问题,请参考以下文章