@property @synthesize @dynamic

@property = ivar + setter + getter

Apple官方文档 这个标题翻译成中文就是 :

属性 = 实例变量 + 存取方法
复制代码

当我们在一个类中定义属性(name)时,系统会自动为我们自动生成一个带下划线的属性 (_name) 并添加这个属性的存取方法 (setter、getter) 。这个过程叫做 autosynthesis (自动合成),在代码的编译期执行。

这就引入了我们接下来要说的 @sythesize 。 但我们还是先说说@property的关键字:

  • 原子性 在操作系统的学习中我们了解到,一个操作具有原子性表明这个操作不可分割,即最基本的操作。这关乎到线程是否安全的问题:多个线程可能同时访问一个属性。如果一个属性不具备原子性,就不会使用同步锁。在iOS开发时,我们极大多数情况都用nonatomic非原子性,而在OS X开发时,会使用到atomic原子性。这样做的原因是在iOS中使用同步锁,性能开销较大,在极少数需要加锁保护线程安全的时候,我们还需要更底层的锁机制。

  • 可读、可写权限 这个特质有两个选项,readwrite和readonly,分别对应读写权限和只读权限。只读权限编译器并不会合成“setter”。默认情况,该特质为readwrite。

内存管理方式

setter方法中赋值的方式,决定了内存管理中引用计数的变化。

这个特质有非常多的选项,我们依次说明。

  • assign:只对简单数据类型进行赋值操作。简单数据类型包括NSInteger、CGFloat等。 weak:不对该属性具备拥有关系。此时的setter方法并不会改变对象的引用计数,如果该对象的拥有者全都release了,那么该对象会彻底释放,该weak指针会被置为空nil。

  • strong:对该属性具备拥有关系。此时的setter方法会retain新的对象,release旧的对象。如果旧的对象之前引用计数为1,那么在这次重新赋值时就会被彻底释放。

  • copy:在setter中,会把该对象复制一份并赋给该属性。我们说复制有两种,『浅拷贝』和『深拷贝』,『浅拷贝』是指对指针的复制,对象本身的内存区域没有变化,新指针指向的还是原有内存区域;『深拷贝』是指对内存区域存储的数据进行的复制,指针会指向新的内存区域。特别注意的是,如果是对可变对象的使用copy特质不会改变引用计数,为深拷贝;如果是不可变对象使用copy特质,就是浅拷贝,引用计数++,此时和strong完全相同。通常情况下,我们对可变对象使用copy特质。当类型为NSString时,我们也应该对其加以copy特质,因为setter中的新值可能是一个NSMutableString。

  • unsafe_unretained:不对该属性具备拥有关系,但和weak的区别在于指针所指向的对象被彻底释放时,该属性的指针不会被置为空nil。

  • retain:这是以前在MRC内存管理方式下的属性,在setter方法中对新值retain。ARC中已经弃用! 关于可变对象要使用copy特质:为了保证封装性,setter方法是属性的设值方法,但可变对象可能会在不经过setter方法的情况下,改变自身的值,这样破坏了原有的封装性。所以要对可变对象做copy操作,深拷贝出一份不可变的对象。

@synthesize 属性自动合成

在以前 @property 要和 @synthesize 搭配使用,iOS 6 之后编译器引入了property autosynthesis,即属性自动合成。编译器会自动给 @property 添加 @synthesize :

@synthesize propertyName = _propertyName;
复制代码

这行代码会在编译期间创造一个带下划线的实例变量名,同时使用这个属性生成getter 和 setter 方法。所以现在,我们的代码中已经很少看到 @synthesize 了。 如果你不喜欢这个带下划线的名字,你也可以自己来指定喜欢的名字:

@synthesize propertyName = propertyName;
复制代码

但带下划线的命名方式已经成为开发者们约定俗成的规范,如果没有特殊要求,还是不要使用@synthesize ,而是直接声明属性@property。

当有自定义的 setter 或 getter 方法时 ,会屏蔽自动生成改方法。

注意

setter 或 getter 是不能同时重写的,否则编译器不会自动生成该属性。

@property与KVO

KVO全称是Key-Value Observing,是Objective-C语言中的一种核心机制,我们一般翻译为键值观察。至于KVO的实现方式,相信很多人也知道,就是继承该类并重写setter方法,当调用setter方法的时候,通知监听器回调。前文说到了,@property可以自动合成setter,所以这里正是它和KVO之间千丝万缕的关系。

我们访问变量有几种方式,遵循Objective-C调用方法的方式访问[self variableName]是一种,self.variableName语法糖是一种,直接访问实例变量_variableName也是一种,这三种方式有什么区别呢?

方法调用[self propertyName]
复制代码

如前文所讲,这种方式正是使用了getter方法,通过getter返回值的来访问到实例变量的值,遵循良好的封装性。

点语法糖self.propertyName
复制代码

正如它的名字一样,这种方式其实只是一种语法糖,其背后根本上还是调用getter方法。

直接访问实例变量_variableName不经过取设方法,直接访问实例变量所在内存位置。优点在于不经过方法调用,速度更快。

同样看一下设值的方式呢,也有如下三种:方法调用

[self setPropertyName]
复制代码

使用setter来设值。 点语法糖

 self.propertyName = @"Name"
复制代码

语法糖,背后同样是setter方法。 直接访问实例变量

 _propertyName = @"Name"
复制代码

不经过设值方法,将新值赋给实例变量所在内存区域。优点还是不经过方法调用,速度更快。然而问题也很明显,用这种方式改变值KVO失效,跳过了setter中内存管理的特质。比如你给某个字符串赋了mutable可变字符串,若该属性内存管理特质为copy,此时通过setter设值会深拷贝一份不可变字符串,但直接访问该实例变量不通过setter会导致没有拷贝出一份不可变字符串。

对于这样的问题,我们大概可以总结出最佳使用姿势:

在需要设值的地方,我们通过setter设值方法设值;当需要取值的时候,我们采用直接访问实例变量的方式。

另外要注意,初始化方法中,没有特殊需要,应该尽量直接访问实例变量。

@dynamic 动态合成

告诉编译器不需要自动合成属性的 setter 和 getter 方法,而由开发之自己动态绑定。 需要注意的是,编译器不会自动生成 属性 所以还需要手动定义属性。

更详尽的资料这里有

@property面试资料

参考资料

转载于:https://juejin.im/post/5c98b8d9f265da6107630051

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值