IOS atomic,nonatomic,assign,copy,retain
1:atomic 和 nonatomic
atomic 和 nonatomic 用来决定编译器生成的getter和setter是否为原子操作。
atomic: 设置成员变量的@property属性时,默认是atomic,提供多线程安全。在多线程还清下,原子操作是必要的,否则有坑呢引起错误的结果,加了atomic,setter函数会变成下面这样:
{lock}
if(property != newValue) {
[property release];
propery = [newValue retain];
}
{unlock}
nonatomic: 非线程安全的。
2:assign
assign的作用:就是直接赋值,简单赋值。对基础数据类型 (NSInteger)和C数据类型(int,float,double,char,short,long,等)赋值(查看oc的数据类型)。它没有对引用计数加一的作用,所以一般不用assign对oc对象进行属性使用。
那为什么九中基础类型可以用assign呢:
基础数据类型: 如short、int、double、long等他们不在【堆】中,可能在【全局区】也可能在【栈】中,根据他们定义的位置而定,而这些内存都是由系统自动管理的,不同于【堆】需要手动释放。所以基本数据类型可以使用assign来生成set方法直接进行赋值。
除却非基本数据类型是对象类型-包装类型时,这类数据一般都在【堆】中被创建,引用计数不正确,可能会导致严重的内存问题。如果要对对象使用assgin,这是赤裸裸的对Object C内存管理机制的挑战,这样做会使属性直接指向赋值者所指向的对象,既不对属性旧对象进行release操作,也不对新对象retain操作。
此标记说明设置器直接进行赋值,这也是默认值。在使用垃圾收集的应用程序中,如果你要一个属性使用assign,且这个类符合NSCopying协议,你就要明确指出这个标记,而不是简单地使用默认值,否则的话,你将得到一个编译警告。这再次向编译器说明你确实需要赋值,即使它是可拷贝的。
结果:iOS中的垃圾处理机制是根据一个对象的索引数来处理的,为0的时候表示没有地方使用该对象,则该对象将被清除,而基本数据类型不属于对象,它的创建和使用都是在内存中,超出对应方法体即被清除,所以不需要使用垃圾处理机制,无需记录索引值,所以使用assgin
3:retain
对其他NSObject和其子类对参数进行release旧值,再retain新值
指定retain会在赋值时唤醒传入值的retain消息。此属性只能用于Objective-C对象类型,而不能用于Core Foundation对象。(原因很明显,retain会增加对象的引用计数,而基本数据类型或者Core Foundation对象都没有引用计数——译者注)。
注意: 把对象添加到数组中时,引用计数将增加对象的引用次数+1。
4:copy
以前在自学OC的时候和实际的项目中,都没有关注这一个小问题,一直使用Strong属性声明,也没发现项目中出现过由于NSString导致的未知Bug。某一天看到南峰子的blog,我也来测试以下看看具体区别。
1.首先分别定义这个两个属性 NSString。
@property (nonatomic,strong)NSString *strongStr;
@property (nonatomic,copy )NSString *copyssStr;
2.然后声明一个不可变的NSSring对象,给这两个属性对象赋值,分别输出他们的指针和内存地址。
- (void)test{
NSString *string = [NSStringstringWithFormat:@"测试文字"];//注释1
self.strongStr = string;
self.copyssStr = string;
NSLog(@"测试文字 String: %p, %p", string, &string);
NSLog(@"Strong属性 String: %p, %p",_strongStr, &_strongStr);
NSLog(@"Copy 属性 String: %p, %p",_copyssStr, &_copyssStr);
}
输出如下:
测试文字 String: 0x7fade9740800, 0x7fff507f06d8
Strong属性 String: 0x7fade9740800, 0x7fade94adf70
Copy 属性 String: 0x7fade9740800, 0x7fade94adf78
结果表明:不管是Strong还是Copy属性的对象,指向的地址都是同一个原来String指向的地址。
如在MRC环境,输出String的引用计数,会看到其引用计数值是3,即Strong操作和Copy操作都会使原字符串对象的引用计数值加了1。
3.接着再声明一个可变的NSSring对象,给这两个属性对象赋值,分别输出他们的指针和内存地址。
只替换注释1 代码为以下代码,输出。
NSMutableString *string = [NSMutableStringstringWithFormat:@"测试文字"];
输出如下:
测试文字 String: 0x7f892bf29760, 0x7fff592ca6d8
Strong属性 String: 0x7f892bf29760, 0x7f892bc42480
Copy 属性 String: 0x7f892be7d2f0, 0x7f892bc42488
结果输出有不同:此时Copy属性字符串已不再指向原来String对象,而是深拷贝了String字符串,且copyssString对象指向这个字符串。
在MRC环境下,输出两者的引用计数,可以看到String对象的引用计数是2,而copyssString对象的引用计数是1。
如果去修改String字符串的话,可以看到:因为strongString与原始String是指向同一对象,所以strongString的值也会跟随着改变(此时strongString的类型实际上是NSMutableString,而不是NSString);而copyssString是指向另一个对象,并不会改变。
4.最后结论
我们知道NSMutableString是NSString的子类,一个NSString指针可以指向NSMutableString对象,strongString指针指向一个可变字符串是正常的。
如上例子可以看出:
1) 当原字符串是NSString时,字符串是不可变的,不管是Strong还是Copy属性的对象,都是指向原对象,Copy操作只是做了次浅拷贝。
2) 当原字符串是NSMutableString时,Strong属性只是增加了原字符串的引用计数,而Copy属性则是对原字符串做了次深拷贝,产生一个新的对象,且Copy属性对象指向这个新的对象,且这个Copy属性对象的类型始终是NSString,而不是NSMutableString,因此其是不可变的。
3) 这里还有一个性能问题,即在原字符串是NSMutableString,Strong是单纯的增加对象的引用计数,而Copy操作是执行了一次深拷贝,所以性能上会有所差异(虽然不大)。如果原字符串是NSString时,则没有这个问题。
所以,在声明NSString属性时,到底是选择strong还是copy,可以根据实际情况来定。不过,一般我们将对象声明为NSString时,都不希望它改变,所以大多数情况下,我们建议用copy,以免因可变字符串的修改导致的一些非预期问题。
https://www.jianshu.com/p/5254f1277dba
https://blog.****.net/weweco/article/details/47107277
mutable类型的(NSMutableString、NSMutableArray、NSMutableDictionary等)这些不能用copy进行修饰 或者copy一个新值,因为的出来的都不再是不可变的了,而有的是另外一种类型,跟上面表中的并不一致。
原因请看下面的连接
关键字copy与mutableCopy的详解、NSMutable属性声明时为什么不能使用copy
5:copy和strong的区别:
不得不说下copy和strong在复制时候的区别,此处不讲引用计数的问题。
copy:拷贝一份不可变副本赋值给属性;所以当原对象值变化时,属性值不会变化;
strong:有可能指向一个可变对象,如果这个可变对象在外部被修改了,那么会影响该属性;
比如在model赋值的时候声明属性,一般字符串我们都用copy,这样省内存并且不可修改,(如果model内容不改变的 情况下。)但是用strong的话就会被修改。看业务操作需求。
6:copy与retain:
Copy其实是建立了一个相同的对象,而retain不是:
1.比如一个NSString 对象,地址为0×1111 ,内容为@”STR”,Copy 到另外一个NSString 之后,地址为0×2222 ,内容相同。
2.新的对象retain为1 ,旧有对象没有变化retain 到另外一个NSString 之后,地址相同(建立一个指针,指针拷贝),内容当然相同,这个对象的retain值+1。
总结:retain 是指针拷贝,copy 是内容拷贝需要考虑到是什么拷贝。
7:assign和weak
assign是指针赋值,不对引用计数操作,使用之后如果没有置为nil,可能就会产生野指针;而weak一旦不进行使用后,就会自己置为nil,所以就不会产生野指针!
copy 修饰string block;
strong 修饰其他oc对象比如array dictionary;
weak 修饰的ui控件 代理属性 ;
assign 基本数据类型 比如cgfloat cgrect
assign 就是赋值,所以大多用于基础类型,因为基础类型只需要赋值就可以了,如果用于指针类型,那么你就需要确定它不需要被引用持有了,在这种情况下,跟weak是一样的
weak 是弱引用,而基础类型不是指针,不能进行引用,所以会编译报错
总结:当你的指针类型成员变量不需要进行强引用的时候,使用assign和weak都可以,比如一些delegate,否则使用strong,而基础类型只能使用assign
assign此特质表明该属性定义了一种“非拥有关系” (nonowning relationship)。为这种属性设置新值时,设置方法既不保留新值,也不释放旧值。此特质同assign类似, 然而在属性所指的对象遭到摧毁时,属性值也会清空(nil out)。 而assign的“设置方法”只会执行针对“纯量类型” (scalar type,例如 CGFloat 或 NSlnteger 等)的简单赋值操作。weak特性要求不保留传入的对象。如果该对象被释放,那么相应的实例变量会被自动赋为nil。这么做可以避免产生悬空指针。悬空指针指向的是不再存在的对象。向悬空指针发送消息通常会导致程序崩溃。相应的存方法会将传入的对象直接赋给实例变量。
另: assigin 可以用非 OC 对象,而 weak 必须用于 OC 对象
8:assign与retain:
1. 接触过C,那么假设你用malloc分配了一块内存,并且把它的地址赋值给了指针a,后来你希望指针b也共享这块内存,于是你又把a赋值给(assign)了b。此时a和b指向同一块内存,请问当a不再需要这块内存,能否直接释放它?答案是否定的,因为a并不知道b是否还在使用这块内存,如果a释放了,那么b在使用这块内存的时候会引起程序crash掉。
2. 了解到1中assign的问题,那么如何解决?最简单的一个方法就是使用引用计数(reference counting),还是上面的那个例子,我们给那块内存设一个引用计数,当内存被分配并且赋值给a时,引用计数是1。当把a赋值给b时引用计数增加到2。这时如果a不再使用这块内存,它只需要把引用计数减1,表明自己不再拥有这块内存。b不再使用这块内存时也把引用计数减1。当引用计数变为0的时候,代表该内存不再被任何指针所引用,系统可以把它直接释放掉。
总结:上面两点其实就是assign和retain的区别,assign就是直接赋值,从而可能引起1中的问题,当数据为int, float等原生类型时,可以使用assign。retain就如2中所述,使用了引用计数,retain引起引用计数加1, release引起引用计数减1,当引用计数为0时,dealloc函数被调用,内存被回收。
9:strong和weak:
一个对象用了strong,表明它被拥有了,比如:nsstrig *str = @“21”; 这里面默认就是strong(在arc环境下),weak的话就是表示随时都可以被释放。对它的关系不是拥有关系。 详细链接(weak和strong)
strong:强引用,ARC模式下与retain同作用,对象的retaincount自动加1
week:弱引用,ARC模式下与assign同作用,非对象类型使用。
注解:
1:
Core Foundation框架 (CoreFoundation.framework) 是一组C语言接口,它们为iOS应用程序提供基本数据管理和服务功能。下面列举该框架支持进行管理的数据以及可提供的服务: