IOS atomic,nonatomic,assign,copy,retain

 

1atomic nonatomic

atomic nonatomic 用来决定编译器生成的gettersetter是否为原子操作。

atomic 设置成员变量的@property属性时,默认是atomic,提供多线程安全。在多线程还清下,原子操作是必要的,否则有坑呢引起错误的结果,加了atomicsetter函数会变成下面这样:

{lock}

    if(property != newValue) {

     [property release];

    propery = [newValue retain];   

    }

{unlock}

nonatomic 非线程安全的。

 

2assign

       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

 

3retain

        对其他NSObject和其子类对参数进行release旧值,再retain新值

        指定retain会在赋值时唤醒传入值的retain消息。此属性只能用于Objective-C对象类型,而不能用于Core Foundation对象。(原因很明显,retain会增加对象的引用计数,而基本数据类型或者Core Foundation对象都没有引用计数——译者注)。

        注意: 把对象添加到数组中时,引用计数将增加对象的引用次数+1。

 

4copy

以前在自学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

IOS atomic,nonatomic,assign,copy,retain

https://blog.csdn.net/weweco/article/details/47107277

mutable类型的(NSMutableString、NSMutableArray、NSMutableDictionary等)这些不能用copy进行修饰 或者copy一个新值,因为的出来的都不再是不可变的了,而有的是另外一种类型,跟上面表中的并不一致。

原因请看下面的连接

关键字copy与mutableCopy的详解NSMutable属性声明时为什么不能使用copy

 

 

 

 

5copystrong的区别:

不得不说下copy和strong在复制时候的区别,此处不讲引用计数的问题。

copy:拷贝一份不可变副本赋值给属性;所以当原对象值变化时,属性值不会变化;

strong:有可能指向一个可变对象,如果这个可变对象在外部被修改了,那么会影响该属性;

比如在model赋值的时候声明属性,一般字符串我们都用copy,这样省内存并且不可修改,(如果model内容不改变的 情况下。)但是用strong的话就会被修改。看业务操作需求。

 

6copyretain

Copy其实是建立了一个相同的对象,而retain不是:

1.比如一个NSString 对象,地址为0×1111 ,内容为@”STR”,Copy 到另外一个NSString 之后,地址为0×2222 ,内容相同。

2.新的对象retain为1 ,旧有对象没有变化retain 到另外一个NSString 之后,地址相同(建立一个指针,指针拷贝),内容当然相同,这个对象的retain值+1。

总结:retain 是指针拷贝,copy 是内容拷贝需要考虑到是什么拷贝。

 

7assignweak

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 对象

 

8assignretain

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函数被调用,内存被回收。

 

9strongweak

一个对象用了strong,表明它被拥有了,比如:nsstrig *str = @“21”; 这里面默认就是strong(在arc环境下),weak的话就是表示随时都可以被释放。对它的关系不是拥有关系。  详细链接(weakstrong

strong:强引用,ARC模式下与retain同作用,对象的retaincount自动加1

week:弱引用,ARC模式下与assign同作用,非对象类型使用。

 

注解:

1:

Core Foundation框架 (CoreFoundation.framework) 是一组C语言接口,它们为iOS应用程序提供基本数据管理和服务功能。下面列举该框架支持进行管理的数据以及可提供的服务: