Apple官方文档 这个标题翻译成中文就是 :面试
属性 = 实例变量 + 存取方法
复制代码
当咱们在一个类中定义属性(name)时,系统会自动为咱们自动生成一个带下划线的属性 (_name) 并添加这个属性的存取方法 (setter、getter) 。这个过程叫作 autosynthesis (自动合成),在代码的编译期执行。安全
这就引入了咱们接下来要说的 @sythesize 。 但咱们仍是先说说@property的关键字:bash
原子性 在操做系统的学习中咱们了解到,一个操做具备原子性代表这个操做不可分割,即最基本的操做。这关乎到线程是否安全的问题:多个线程可能同时访问一个属性。若是一个属性不具有原子性,就不会使用同步锁。在iOS开发时,咱们极大多数状况都用nonatomic非原子性,而在OS X开发时,会使用到atomic原子性。这样作的缘由是在iOS中使用同步锁,性能开销较大,在极少数须要加锁保护线程安全的时候,咱们还须要更底层的锁机制。app
可读、可写权限 这个特质有两个选项,readwrite和readonly,分别对应读写权限和只读权限。只读权限编译器并不会合成“setter”。默认状况,该特质为readwrite。post
setter方法中赋值的方式,决定了内存管理中引用计数的变化。性能
这个特质有很是多的选项,咱们依次说明。学习
assign:只对简单数据类型进行赋值操做。简单数据类型包括NSInteger、CGFloat等。 weak:不对该属性具有拥有关系。此时的setter方法并不会改变对象的引用计数,若是该对象的拥有者全都release了,那么该对象会完全释放,该weak指针会被置为空nil。ui
strong:对该属性具有拥有关系。此时的setter方法会retain新的对象,release旧的对象。若是旧的对象以前引用计数为1,那么在此次从新赋值时就会被完全释放。atom
copy:在setter中,会把该对象复制一份并赋给该属性。咱们说复制有两种,『浅拷贝』和『深拷贝』,『浅拷贝』是指对指针的复制,对象自己的内存区域没有变化,新指针指向的仍是原有内存区域;『深拷贝』是指对内存区域存储的数据进行的复制,指针会指向新的内存区域。特别注意的是,若是是对可变对象的使用copy特质不会改变引用计数,为深拷贝;若是是不可变对象使用copy特质,就是浅拷贝,引用计数++,此时和strong彻底相同。一般状况下,咱们对可变对象使用copy特质。当类型为NSString时,咱们也应该对其加以copy特质,由于setter中的新值多是一个NSMutableString。spa
unsafe_unretained:不对该属性具有拥有关系,但和weak的区别在于指针所指向的对象被完全释放时,该属性的指针不会被置为空nil。
retain:这是之前在MRC内存管理方式下的属性,在setter方法中对新值retain。ARC中已经弃用! 关于可变对象要使用copy特质:为了保证封装性,setter方法是属性的设值方法,但可变对象可能会在不通过setter方法的状况下,改变自身的值,这样破坏了原有的封装性。因此要对可变对象作copy操做,深拷贝出一份不可变的对象。
在之前 @property 要和 @synthesize 搭配使用,iOS 6 以后编译器引入了property autosynthesis,即属性自动合成。编译器会自动给 @property 添加 @synthesize :
@synthesize propertyName = _propertyName;
复制代码
这行代码会在编译期间创造一个带下划线的实例变量名,同时使用这个属性生成getter 和 setter 方法。因此如今,咱们的代码中已经不多看到 @synthesize 了。 若是你不喜欢这个带下划线的名字,你也能够本身来指定喜欢的名字:
@synthesize propertyName = propertyName;
复制代码
但带下划线的命名方式已经成为开发者们约定俗成的规范,若是没有特殊要求,仍是不要使用@synthesize ,而是直接声明属性@property。
当有自定义的 setter 或 getter 方法时 ,会屏蔽自动生成改方法。
setter 或 getter 是不能同时重写的,不然编译器不会自动生成该属性。
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设值方法设值;当须要取值的时候,咱们采用直接访问实例变量的方式。
另外要注意,初始化方法中,没有特殊须要,应该尽可能直接访问实例变量。
告诉编译器不须要自动合成属性的 setter 和 getter 方法,而由开发之本身动态绑定。 须要注意的是,编译器不会自动生成 属性 因此还须要手动定义属性。
更详尽的资料这里有