@property分为@property和@dynamic或者@synthesize:
@property
,写在.h 文件的@interface
中,完成属性的声明@dynamic
或者@synthesize
,写在.m文件的@implementation
中,完成属性时实现@dynamic
的意思是告诉编译器,该属性的实例变量和存取方法由用户自己实现,不自动生成。
用@property声明的NSString(或NSArray,NSDictionary)经常使用copy关键字,为什么?如果改用strong关键字,可能造成什么问题?
- 因为父类指针可以指向子类对象,使用
copy
的目的是为了让本对象的属性不受外界的影响,使用copy
无论传入的是一个可变的对象还是一个不可变的对象,我本身持有的都是一个不可变的副本。- 如果使用的是
strong
,那么这个属性就有可能指向一个可变的对象,如果这个可变对象在外界被修改了,那么会影响该属性。
//.h 文件中
@property (nonatomic) NSInteger age;
@property (nonatomic, copy) NSString *name;
@property (nonatomic, strong) NSNUmber *number;
// .m文件中
@synthesize name = _name,age = _age, number = _number;
// 该语句不写就是默认自动生成的,代表所有 setter 和 getter 方法的实现
在平时的使用中,在.h中进行@property
后,会自动在.m中生成相对应的@synthesize
。
@synthesize 的作用:
1.添加属性对应的成员变量
2.规定了该属性声明的 setter getter 方法所操作的成员变量
3.如果@synthesize 省略不写,则自动生成对应属性的 setter 和 getter 方法,
默认操作的成员变量是‘_’+属性名
4.检测手动实现了@synthesize,就会按照你的要求生成成员变量名称并生成对应的 setter
和 getter 方法,
如@synthesize name = _myName; 这样成员变量就是 _myNam 了
5.在以下几种情况下不会自动合成setter 和 getter 方法,而且也不会检测成员变量是否存在,
也就不会帮助我们生成对应的成员变量,则需要我们自己添加成员变量:
1>同事重写了 setter 和 getter 时;
2>重写了只读属性的 getter 时;
3>使用了@dynamic时;
4>在@protocol 中定义的所有属性;
5>在 category 中定义的所有属性;
6>重写的属性,当你在子类中重写了父类中的属性,你必须使用@synthesize来手动合成ivar。
关于@property的说明:
1.读写特性
(1)
readwrite(可读可写)
默认属性,既生成 setter 方法,又生成 getter 方法
(2)readonly(只读)
,只生成getter
方法,不会生成setter
方法
(3)setter = 方法名
,给生成的setter
方法起一个其他的方法名
(4)getter = 方法名
,给生成的getter
方法起一个其他的方法名
2.原子特性
atomic(原子特性)
默认属性,保证线程安全,也就是当前的实例变量只能由一个线程访问,在这个过程中有一个加锁解锁的过程(其实然并卵,这个操作并不能根本上保证线程的安全,还会因为加锁解锁的操作增加损耗。)
对于
atomic
的属性,系统生成的getter/setter
会保证 get、set 操作的完整性,不受其他线程影响。比如,线程 A 的getter
方法运行到一半,线程 B 调用了 setter:那么线程 A 的getter
还是能得到一个完好无损的对象。
而
nonatomic
就没有这个保证了。所以,nonatomic
的速度要比atomic
快。
不过
atomic
可并不能保证线程安全。如果线程 A 调了getter
,与此同时线程 B 、线程 C 都调了setter
——那最后线程 A get 到的值,3种都有可能:可能是 B、C set 之前原始的值,也可能是 B set 的值,也可能是 C set 的值。同时,最终这个属性的值,可能是 B set 的值,也有可能是 C set 的值。
3.语义特性
(1)
assign
默认属性,一般用来修饰非对象类型的属性,但是也可以修饰对象类的属性
(2)retain
针对于对象,会将对象的引用计数加1 ,MRC下的,在ARC下被strong取代
(3)copy
针对于对象,将对象拷贝一份,产生一个新的对象,多用于像NSString
,NSDictionary
,NSArray
这样本身有可变类型的。可变类型的对象不能用copy
,copy
过来会是不可变类型
(4)weak
弱引用,针对于对象,不持有对象,当对象被释放时,自动将对象置为nil
,用于防止循环引用。
copy与retain
copy
是建立一个相同的对象,retain
不是copy
是内容拷贝,retain
是指针拷贝。copy
是内容的拷贝 ,对于像NSString
,的确是这样,但是如果copy
的是一个NSArray
呢?这时只是copy
了指向array
中相对应元素的指针.这便是所谓的"浅复制".copy
的情况:NSString *newPt = [pt copy];
此时会在堆上重新开辟一段内存存放@"abc"
比如0X1122
内容为@"abc"
同时会在栈上为newPt
分配空间 比如地址:0Xaacc
内容为0X1122
因此retainCount
增加1供newPt
来管理0X1122
这段内存;
assign与retain:
assign
: 简单赋值,不更改索引计数;assign
的情况:NSString *newPt = [pt assing]
;
此时newPt
和pt
完全相同 地址都是0Xaaaa
内容为0X1111
即newPt
只是pt
的别名,对任何一个操作就等于对另一个操作, 因此retainCount
不需要增加;
3.
assign
就是直接赋值;
4.retain
使用了引用计数,retain
引起引用计数加1,release
引起引用计数减1,当引用计数为0时,dealloc
函数被调用,内存被回收;
5.retain
的情况:NSString *newPt = [pt retain];
此时newPt
的地址不再为0Xaaaa
,可能为0Xaabb
但是内容依然为0X1111
。 因此newPt
和pt
都可以管理"abc"所在的内存,因此retainCount
需要增加1 ;
readonly:
- 属性是只读的,默认的标记是读写,如果你指定了只读,在
@implementation
中只需要一个读取器。或者如果你使用@synthesize
关键字,也是有读取器方法被解析
readwrite:
- 说明属性会被当成读写的,这也是默认属性。设置器和读取器都需要在
@implementation
中实现。如果使用@synthesize
关键字,读取器和设置器都会被解析;
nonatomic:
- 非原子性访问,对属性赋值的时候不加锁,多线程并发访问会提高性能。如果不加此属性,则默认是两个访问方法都为原子型事务访问;
weak and strong property (强引用和弱引用的区别):
weak
和strong
属性只有在你打开ARC时才会被要求使用,这时你是不能使用retain release autorelease
操作的,因为ARC
会自动为你做好这些操作,但是你需要在对象属性上使用weak
和strong
,其中strong
就相当于retain
属性,而weak
相当于assign
。- 只有一种情况你需要使用
weak(默认是strong)
,就是为了避免retain cycles
(就是父类中含有子类{父类retain了子类},子类中又调用了父类{子类又retain
了父类},这样都无法release
)- 声明为
weak
的指针,指针指向的地址一旦被释放,这些指针都将被赋值为nil
。这样的好处能有效的防止野指针。
ARC(Automatic Reference Counting):
- 就是代码中自动加入了
retain/release
,原先需要手动添加的用来处理内存管理的引用计数的代码可以自动地由编译器完成了。
该机能在iOS 5/ Mac OS X 10.7
开始导入,利用Xcode4.2
以后可以使用该特性。
strong,weak,copy 具体用法:
- 具体一点:
IBOutlet
可以为weak
,NSString
为copy
,Delegate
一般为weak
,其他的看情况。一般来说,类“内部”的属性设置为strong
,类“外部”的属性设置为weak
。说到底就是一个归属权的问题。小心出现循环引用导致内存无法释放。
- 用
assign
声明的delegate
,在delegate
指向的对象销毁后,delegate
中依然会保留之前对象的地址,即delegate
变成野指针。- 用
weak
声明的delegate
,在delegate
指向的对象销毁后,delegate = nil
- 不用
ARC
的话就会看到很多retian
。- 如果你写了
@synthesize abc = _abc
;的话,系统自动帮你声明了一个_abc
的实例变量。