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

反编译做了什么

  1. OBJC_IVAR_ 属性名称 :该属性的“偏移量” (offset),这个偏移量是“硬编码” (hardcode),表示该变量距离存放对象的内存区域的起始地址有多远。
  2. setter 与 getter 方法对应的实现函数
  3. ivar_list :成员变量列表
  4. method_list :方法列表
  5. prop_list :属性列表
    也就是说我们每次在增加一个属性,系统都会在 ivar_list 中添加一个成员变量的描述,在 method_list 中增加 setter 与 getter 方法的描述,在属性列表中增加一个属性的描述,然后计算该属性在对象中的偏移量,然后给出 setter 与 getter 方法对应的实现,在 setter 方法中从偏移量的位置开始赋值,在 getter 方法中从偏移量开始取值,为了能够读取正确字节数,系统对象偏移量的指针类型进行了类型强转.

默认情况

  • 对应基本数据类型默认关键字是 atomic,readwrite,assign
  • 对于普通的 Objective-C 对象 atomic,readwrite,strong

@property

  1. 原子性— nonatomic 特质
  2. 读/写权限—readwrite(读写)、readonly (只读)
  3. 内存管理语义—assign、strong、 weak、unsafe_unretained、copy
  4. 方法名—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 合成实例变量的规则,有以下几点:

  1. 如果指定了成员变量的名称,会生成一个指定的名称的成员变量,
  2. 如果这个成员已经存在了就不再生成了.
  3. 如果是 @synthesize xjy; 还会生成一个名称为xjy的成员变量,也就是说:如果没有指定成员变量的名称会自动生成一个属性同名的成员变量,
  4. 如果是 @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的主要内容,如果未能解决你的问题,请参考以下文章

-对象消息运行期)第6条:理解“属性”这一概念

iOS-深入理解(转载)

iOS runtime探究: 从runtime开始理解OC的属性property

深入理解RunLoop

深入理解RunLoop(转载)

代码精进之路——关键的六个特质