automic&nonautomic, strong&weak, assign&retain区别

版权声明:Coder Bruce https://blog.csdn.net/bruceyou1990/article/details/51278825

属性的声明方法
@property (<#attributes#>) <#type#> <#name#>;
@property (<#属性#>) <#对象类型#> <#对象名字#>;

这个属性其实呢是对底层isa指针的一些操作

NSString *brucePoint = [[NSString alloc] initWithString:@"bruceString"];

1 在堆上分配一段内存用来存储@”bruceString” 比如:内存地址为:0X1234 内容为 “bruceString”
2 在栈上分配一段内存用来存储brucePoint指针 比如:地址为:0Xaaaa 内容自然为0X1234

1.automic&nonautomic

atomic和nonatomic区别用来决定编译器生成的getter和setter是否为原子操作。atomic提供多线程安全,是描述该变量是否支持多线程的同步访问,如果选择了atomic 那么就是说,系统会自动的创建lock锁,锁定变量。nonatomic禁止多线程,变量保护,提高性能。

@property (nonatomic, retain) UIView * myView;

这里定义了一个UIView类型的属性,不需要多线程操作这个UIView,所以用nonatomic.当对myView 赋值的时候原来的UIView对象retainCount会加1

atomic:默认是有该属性的,这个属性是为了保证程序在多线程情况下,编译器会自动生成一些互斥加锁代码,避免该变量的读写不同步问题。

//线程锁
{lock}  
if (property != newValue) {   
    [property release];   
    property = [newValue retain];   
}  
{unlock}  

nonatomic:如果该对象无需考虑多线程的情况,请加入这个属性,这样会让编译器少生成一些互斥加锁代码,可以提高效率。

  1. 那么什么时候用nonatomic(非原子)atomic(原子)
    一般iOS程序中我们都选择属性为nonatomic(非原子)非线程安全。原因是:在iOS中使用同步锁atomic(原子)的开销比较大, 这会带来性能问题。而且实际情况一般情况并不要求属性必须是“原子的”。
    atomic(原子)的线程一定是安全的吗?并没有,“线程安全”(thread safety),若要实现“线程安全”的操作,还需采用更为深层的锁定机制才醒。

例如:一个线程在连续多次读取某个属性值的过程中有别的线程在同时改写该值,那么即便将属性声明为atomic,也还是会读取到不同的属性值。
copy(复制)&retain(持有)&assign(分配)&week(复制)

2.assign&retain&copy&strong&weak

一、assign属性
  当数据类型为int、float等原生类型时,可以使用assign,否则可能导致内存泄露。例如当使用malloc分配了一块内存,并把它的地址赋值给了指针a,后来如果希望指针b也共享这块内存,于是讲a赋值给(assgin)b。这时就用到了assgin,此时a和b指向同一块内存。但是现在问题出现了,当a不再需要这块内存时,能都直接释放呢?肯定是不能的,因为a并不知道b是否还在使用这块内存,如果a释放了,那么b在使用这块内存的时候引起程序crash掉。

@property (nonatomic, assign) int number;

这里定义了一个int类型的属性, 那么这个int是简单数据类型,本身可以认为就是原子访问,所以用nonatomic, 不需要进行引用计数,所以用assign。 适用于所有简单数据类型。  

  
 二、retain属性 (MRC)
  retain属性就是为了解决上述问题而提出的,使用了引用计数(reference counting),还是上面那个例子,我们给那块内存设一个引用计数,当内存呗分配并且赋值给a时,引用计数是1.当把a赋值给b时引用计数增加到2.这时如果a不再使用这块内存,它只需要把引用计数减1,表明自己不再拥有这块内存。b不再使用这块内存时也把引用计数减1.当引用计数变为0的时候,代表该内存不再被任何指针所引用,系统可以直接释放掉。此时系统自动调用dealloc函数,内存被回收。

三、copy属性
复制一个对象本持有它,这个时候你持有了嘛,引用计数为1,

@property (nonatomic, copy) NSString * myString;

这里定义了一个NSString类型的属性,不需要原子操作,所以用nonatomic.
为什么需要copy,而不是retain呢! 因为如果对myString赋值原字符串是一个可变的字符串(NSMutableString)对象的话,用retain的话,当原字符串改变的时候你的myString属性也会跟着变掉。我想你不希望看到这个现象。 实际上博主测试, 如果原来的字符串是NSString的话,也只是retain一下,并不会copy副本

四、strong属性 (ARC )
  引用计数加1,只有在这个属性被自己设置为nil的情况下才会销毁(除非还有别的也持有这个指针),retain是创建一个指针,引用对象计数加1。
五、weak属性
  你不想控制这个属性的生命周期,弱引用/浅拷贝(引用计数不加1),必须有一个对象strong/或者持有它,换句话来说如果原本持有他的对象不再持有(release),他就会被释放nil掉
  
  经常用到week的@property有: 

代理属性,我们经常用week属性来防止循环引用(delegate properties, which are often referenced weakly to avoid retain cycles, and)

view controller’s的子类或者控制器应用被他们的views强引用(subviews/controls of a view controller’s main view because those views are already strongly held by the main view.)

3.copy与retain的区别

copy是创建一个新对象,retain是创建一个指针,引用对象计数加1。
eg: 一个NSString 对象,地址为0×1111 ,内容为@”STR”

Copy 到另外一个NSString 之后,地址为0×2222 ,内容相同,新的对象retain为1 ,旧有对象没有变化

retain 到另外一个NSString 之后,地址相同(建立一个指针,指针拷贝),内容当然相同,这个对象的retain值+1

记住:retain 是指针拷贝,copy 是内容拷贝。

具体应用

下面分别看下assign retain copy
assign的情况:NSString *newPt = [pt assing];
此时newPt和pt完全相同 地址都是0Xaaaa 内容为0X1111 即newPt只是pt的别名,对任何一个操作就等于对另一个操作。 因此retainCount不需要增加。
retain的情况:NSString *newPt = [pt retain];
此时newPt的地址不再为0Xaaaa,可能为0Xaabb 但是内容依然为0X1111。 因此newPt 和 pt 都可以管理”abc”所在的内存。因此 retainCount需要增加1
copy的情况:NSString *newPt = [pt copy];
此时会在堆上重新开辟一段内存存放@”abc” 比如0X1122 内容为@”abc 同时会在栈上为newPt分配空间 比如地址:0Xaacc 内容为0X1122 因此retainCount增加1供newPt来管理0X1122这段内存

附录

创建一个BruceClass类

//接口文件
@interface BruceClass : NSObject
@property (nonatomic, assign) int         number;
@property (nonatomic, copy)   NSString  * myString;
@property (nonatomic, retain) UIView    * myView;
@end
//实现文件
@implementation BruceClass
@synthesize number;
@synthesize myString;
@synthesize myView;
//释放内存
-(void) dealloc
{
[myString release];  //copy的属性需要release;
[myView release];    //retain的属性需要release;
[super dealloc]; //传回父对象
}
@end

假如你有一段代码创建了一个BruceClass对象

MyClass * instance  = [BruceClass alloc] init];``
//number赋值,没什么可说的, 简单数据类型就这样
instance.number = 1;
//创建一个可变字符串
NSMutableString * string = [NSMutableString stringWithString:@"hello"];
 //对myString赋值
instance.myString = string;                 
 //往string追加文本
[string appendString:@" world!"];  
//此处string已经改变, 输出为 “hello world!” 
NSLog(@”%@”,string);      
//输出myString,你会发现此处输出仍然为 “hello” 因为 myString在string改变之前已经copy了一份副本                  
NSLog(@”%@”,instance.myString);  

UIView * view = [[UIView alloc] init];
NSLog(@”retainCount = %d”,view.retainCount);
//输出view的引用计数, 此时为1
instance.myView = view; //对myView属性赋值
NSLog(@”retainCount = %d”,view.retainCount);
//再次输出view的引用计数, 此时为2,因为myView对view进行了一次retain。
[view release];
//此处虽然view被release释放掉了,但myView对view进行了一次retain,那么myView保留的UIView的对象指针仍然有效。
[instance release] ;

2016.08.06 更新日志
strong 是主要用于 ARC
retain 是主要用于 MRC

猜你喜欢

转载自blog.csdn.net/bruceyou1990/article/details/51278825