ios-基础之【13】-iOS的@property的详细解读
2016-12-06 11:15
274 查看
原文:http://blog.talisk.cn/blog/2016/03/05/iOS-@property/
@property的前世今生
@property与@synthesize
@property与取值、设值方法
@property的特质
原子性
可读、可写权限
内存管理方式
setter和getter方法名
特别注意
@property与KVO
@property在Objective-C中,我们称为属性。它与实例变量有密不可分的关系,从而会涉及到内存管理、KVO等诸多细节问题。
看了很多代码,不少人对使用
鄙人水平有限,若文章内容存在错误,欢迎您在评论中指正,我会感激不尽。
面向对象思想有一个非常重要的性质,封装性。上述的权限声明正是为了保障封装性而产生的。即对外只公开需要公开的属性和方法,内部隐藏起实现细节和并没有必要对外公开的属性和方法。而为了这个封装性,通常情况下我们还会定义为此定义
@property是一条编译器指令,用于编译器为实例变量自动生成
举个栗子:
这是.h头文件的内容,希望暴露出的属性写在这里,其他类可以通过.语法来访问
对于不希望暴露给外界的属性,写在实现文件中,以extension的形式出现。.m实现文件内容如下:
这样,我们可以在所有import了.h文件的地方使用
查了一下资料,在Xcode 4.0版本之前,确实是不写
4000
这样一段属性声明,等同于自动合成的结果:
关于点语法,其实只是Objective-C提供的语法糖。我们在使用点语法访问属性的时候,其实是调用了取值方法“getter”。
这个特质有非常多的选项,我们依次说明。
关于可变对象要使用
用法直接举例:
setter设值方法用法和上面例子用法相同,只是不太常用。
我们访问变量有几种方式,遵循Objective-C调用方法的方式访问
方法调用
如前文所讲,这种方式正是使用了getter方法,通过getter返回值的来访问到实例变量的值,遵循良好的封装性。
点语法糖
正如它的名字一样,这种方式其实只是一种语法糖,其背后根本上还是调用getter方法。
直接访问实例变量
同样看一下设值的方式呢,也有如下三种:
方法调用
使用setter来设值。
点语法糖
语法糖,背后同样是setter方法。
直接访问实例变量
不经过设值方法,将新值赋给实例变量所在内存区域。优点还是不经过方法调用,速度更快。然而问题也很明显,用这种方式改变值KVO失效,跳过了setter中内存管理的特质。比如你给某个字符串赋了mutable可变字符串,若该属性内存管理特质为
对于这样的问题,我们大概可以总结出最佳使用姿势:
在需要设值的地方,我们通过setter设值方法设值;当需要取值的时候,我们采用直接访问实例变量的方式。
另外要注意,初始化方法中,没有特殊需要,应该尽量直接访问实例变量。
Objective-C的@property的详细解读
文章目录@property的前世今生
@property与@synthesize
@property与取值、设值方法
@property的特质
原子性
可读、可写权限
内存管理方式
setter和getter方法名
特别注意
@property与KVO
@property在Objective-C中,我们称为属性。它与实例变量有密不可分的关系,从而会涉及到内存管理、KVO等诸多细节问题。
看了很多代码,不少人对使用
@property的使用习惯和认识也有一些不合理的地方,所以写这篇文章来谈谈
@property的正确使用姿势。
鄙人水平有限,若文章内容存在错误,欢迎您在评论中指正,我会感激不尽。
@property的前世今生
在Java、C++中我们一般都会为实例变量添加访问权限声明@public、
@private和
@protect,但在Objective-C中,我们通常不会为实例变量添加权限声明的。由于Objective-C是一门动态的语言,允许开发者随时(甚至在运行时)向应用添加实例变量,所以在Objective-C中,没有采用这种方法。
面向对象思想有一个非常重要的性质,封装性。上述的权限声明正是为了保障封装性而产生的。即对外只公开需要公开的属性和方法,内部隐藏起实现细节和并没有必要对外公开的属性和方法。而为了这个封装性,通常情况下我们还会定义为此定义
setter和
getter方法。
@property是一条编译器指令,用于编译器为实例变量自动生成
setter和
getter方法。通常在Objective-C中,我们会在头文件中将需要暴露出来的实例变量的属性
@property给出,不希望暴露的,可将该实例变量的属性
@property写在类的实现文件的扩展(extension)中。
举个栗子:
这是.h头文件的内容,希望暴露出的属性写在这里,其他类可以通过.语法来访问
name和
birthday两个属性。
1 2 3 4 5 | @interface Student : NSObject @property (nonatomic, copy) NSString *name; @end |
1 2 3 4 56 | @interface Student () @property (nonatomic, copy) NSString *girlFriendName; @end @implementation Student @end |
name这个字符串,权限类似于C++中的Public;
girlFriendName只能在
Student类的内部使用,即使是
Student的子类也不能使用,权限类似于C++中的Private。
@property与@synthesize
在有些教程中,我们会发现有提到@property与
@synthesize要配对使用。然后我们尝试不写
@synthesize,于是发现仍能正常执行,甚至正常通过“.”点语法(语法糖)和取值、设值方法来访问实例变量。那么问题来了,
@synthesize的意义到底是什么呢?
查了一下资料,在Xcode 4.0版本之前,确实是不写
@synthesize不行,后来版本中的LLVM优化了这个语法,只写
@property不写
@synthesize也可以自动合成
setter和
getter方法。自动合成的属性所对应的实例变量名就是属性名在前面加“_”,如果我们不想使用这个实例变量名,就可以使用
@synthesize来指定实例变量名字。如
@synthesize content = textContent;。但实例变量中使用下划线作为开头,已经成了广大开发者约定俗成的习惯,所以如果没有特殊需求,建议还是遵循规范,不需要写
@synthesize,直接声明属性
@property。
@property与取值、设值方法
这一块直接举个例子,一目了然地说明:1 2 3 4 56 | @interface Student : NSObject @property (nonatomic, copy) NSString *name; @property (nonatomic, strong) NSDate *birthday; @end |
这样一段属性声明,等同于自动合成的结果:
1 2 3 4 56 | @interface Student : NSObject - (NSString *)name; - (NSDate *)birthday; - (void)setName:(NSString *)name; - (void)setBirthday:(NSDate *)birthday; @end |
@property的特质
我们在前面的例子里,@property后跟了一个括号,里面写了几个关键字,这几个关键字正是
@property的特质。特质规定了自动合成的“setter”与“getter”的具体细节。特质具有四类:原子性、可读可写、内存管理、取设值方法名。
原子性
在操作系统的学习中我们了解到,一个操作具有原子性表明这个操作不可分割,即最基本的操作。这关乎到线程是否安全的问题:多个线程可能同时访问一个属性。如果一个属性不具备原子性,就不会使用同步锁。在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操作,深拷贝出一份不可变的对象。
setter和getter方法名
可以通过这个特质来指定取值、设值方法的方法名。用法直接举例:
1 | @property (nonatomic, assign, getter = isOpen) BOOL open; |
特别注意
这些特质都指明了自动合成取设值方法的诸多细节,如果我们开发者想自己实现取设值方法,必须要遵守@property中声明的特质,否则会造成误会,甚至奇怪的bug。
@property与KVO
KVO全称是Key-Value Observing,是Objective-C语言中的一种核心机制,我们一般翻译为键值观察。至于KVO的实现方式,相信很多人也知道,就是继承该类并重写setter方法,当调用setter方法的时候,通知监听器回调。前文说到了,@property可以自动合成setter,所以这里正是它和KVO之间千丝万缕的关系。
我们访问变量有几种方式,遵循Objective-C调用方法的方式访问
[self variableName]是一种,
self.variableName语法糖是一种,直接访问实例变量
_variableName也是一种,这三种方式有什么区别呢?
方法调用
[self variableName]
如前文所讲,这种方式正是使用了getter方法,通过getter返回值的来访问到实例变量的值,遵循良好的封装性。
点语法糖
self.variableName
正如它的名字一样,这种方式其实只是一种语法糖,其背后根本上还是调用getter方法。
直接访问实例变量
_variableName不经过取设方法,直接访问实例变量所在内存位置。优点在于不经过方法调用,速度更快。
同样看一下设值的方式呢,也有如下三种:
方法调用
[self setVariableName]
使用setter来设值。
点语法糖
self.variableName = @"talisk"
语法糖,背后同样是setter方法。
直接访问实例变量
_variableName = @"talisk"
不经过设值方法,将新值赋给实例变量所在内存区域。优点还是不经过方法调用,速度更快。然而问题也很明显,用这种方式改变值KVO失效,跳过了setter中内存管理的特质。比如你给某个字符串赋了mutable可变字符串,若该属性内存管理特质为
copy,此时通过setter设值会深拷贝一份不可变字符串,但直接访问该实例变量不通过setter会导致没有拷贝出一份不可变字符串。
对于这样的问题,我们大概可以总结出最佳使用姿势:
在需要设值的地方,我们通过setter设值方法设值;当需要取值的时候,我们采用直接访问实例变量的方式。
另外要注意,初始化方法中,没有特殊需要,应该尽量直接访问实例变量。
相关文章推荐
- 黑马程序员_ios基础总结13_OCFoundation2
- 详细解读 iOS应用程序
- [黑马IOS自学第十一篇]OC语言基础,SELF关键字,面向对象概念,类学习多态,继承@property等
- 泛型超详细解读(一):泛型基础
- iOS基础知识之@property 和 Ivar 的区别
- 安卓开发13:ListView详细解读
- iOS开发学习专题-基础知识(一)NSString NSMutableString NSRange NSNotFound详细使用方式
- IOS控制器管理详细解读
- IOS开发基础Object-C(06)—@property 和@synthesize语法
- 蓝鸥iOS从零基础到精通就业-C语言入门 13结构体一
- [iOS基础控件 - 6.8] 各种数据类型的@property属性
- 黑马程序员--ios基础--oc内存管理--@property参数、循环引用、autorelease
- 黑马程序员——零基础学习iOS开发——13 Foundation框架
- IOS开发基础Object-C(09)—OC内存管理(3)-@property的参数详解
- iOS开发基础之ivars(实例变量)与@property(属性)
- iOS 详细解释@property和@synthesize关键字
- 【iOS基础控件 - 13】【Demo 】QQ好友列表TableView
- iOS开发UI基础—13在UIImageView中添加按钮以及Tag的参数说明
- ios-基础用法之【3】-@property、@synthesize和dynamic的用法
- iOS @property探究(一): 基础详解