在定义属性的时候,都需要为属性加特性,属性的特性决定了属性在原子性、读写权限以及内存管理的特性。
第一类:原子性(atomic,nonatomic)
(1)atomic(默认)
atomic意为操作是原子的,即是只能通过单个线程去访问实例变量。 如何去限制只有一个线程去访问实例变量? 这种特性下,由系统自动生成的setter/getter方法会加锁,但是即使这样也不能一定保证线程安全,而且比较耗时,所以我们一般不选用atomic。
(2)nonatomic
nonatomic正好与atomic相反,表示是多线程的,多个线程可以同时去访问实例变量。在这种特性下,系统自动生成的setter/getter方法不会上锁,所以我们一般选用这个特性。
第二类:读写权限(readwrite/readonly)
(1)用readwrite修饰的属性,拥有getter和setter方法
(2)用readonly修饰的属性,只拥有getter方法,没有setter方法。
第三类:内存管理
再介绍下面的特性之前,先了解一下“引用计数”。
(1) assign
assign主要应用于代表简单数据类型的property,比如int,float等。 如果这个用assign属性修饰的property代表一个指向对象的指针,那么当这个指针指向某个对象时,这个对象的引用计数不应该被改变。也就是说,用assign属性修饰的property,不应该持有一个对象。
(2)strong
strong强引用,当引用时,引用计数会加1,分配内存地址。
(3)weak
不会改变引用计数,weak比较常用的地方就是delegate属性的设置。
(4)copy
修饰NSString、NSArray、NSDictionary等有对应可变类型的对象,建立一个索引计数为1的对象,然后释放旧对象。所以被copy的对象引用计数不会加1。
(5)retain
retain不能修饰用来代表简单数据类型的property,如果一个property被retain修饰,这代表着这个property应该持有它所指向的对象。
当采用strong或者copy对NSString进行修饰时:
@property(nonatomic, strong) NSString *strongStr;
@property(nonatomic, copy) NSString *copyyStr;
第一种:
/ 第一种场景:用NSString直接赋值
NSString *originStr1 = [NSString stringWithFormat:@"hello,everyone"];
_strongStr = originStr1;
_copyyStr = originStr1;
NSLog(@"第一种场景:用NSString直接赋值");
NSLog(@" 对象地址 对象指针地址 对象的值 ");
NSLog(@"originStr: %p , %p , %@", originStr1, &originStr1, originStr1);
NSLog(@"strongStr: %p , %p , %@", _strongStr, &_strongStr, _strongStr);
NSLog(@" copyyStr: %p , %p , %@", _copyyStr, &_copyyStr, _copyyStr);
结果: 
结论:这种情况下,不管是用strong还是copy修饰的对象,其指向的地址都是originStr的地址。
第二种:
NSMutableString *originStr2 = [NSMutableString stringWithFormat:@"hello,everyone"];
_strongStr = originStr2;
_copyyStr = originStr2;
[originStr2 setString:@"hello,QiShare"];
NSLog(@"第二种场景:用NSMutableString直接赋值");
NSLog(@" 对象地址 对象指针地址 对象的值 ");
NSLog(@"originStr: %p , %p , %@", originStr2, &originStr2, originStr2);
NSLog(@"strongStr: %p , %p , %@", _strongStr, &_strongStr, _strongStr);
NSLog(@" copyyStr: %p , %p , %@", _copyyStr, &_copyyStr, _copyyStr);
结果: 
从这个打印的结果可能会有这样的疑问:为什么不论是用strong还是copy修饰的对象,其指针指向的地址依然还是originStr的地址?为什么_copyyStr的值会变成“hello,QiShare”呢?不应该是“hello,everyone”吗?这个等等解释…
第三种:
NSMutableString *originStr3 = [NSMutableString stringWithFormat:@"hello,everyone"];
self.strongStr = originStr3;
self.copyyStr = originStr3;
[originStr3 setString:@"hello,QiShare"];
NSLog(@"第三种场景:用NSMutableString点语法赋值");
NSLog(@" 对象地址 对象指针地址 对象的值 ");
NSLog(@"originStr: %p , %p , %@", originStr3, &originStr3, originStr3);
NSLog(@"strongStr: %p , %p , %@", _strongStr, &_strongStr, _strongStr);
NSLog(@" copyyStr: %p , %p , %@", _copyyStr, &_copyyStr, _copyyStr);

第三种在赋值的时候用了点语法,而不是直接赋值。当我们用@property来声明属性变量时,编译器会自动为我们生成一个以下划线加属性名命名的实例变量(@synthesize copyyStr = _copyyStr),并且生成其对应的getter、setter方法。 当我们用self.copyyStr = originStr赋值时,会调用coppyStr的setter方法,而_copyyStr = originStr 赋值时给_copyyStr实例变量直接赋值,并不会调用copyyStr的setter方法,而在setter方法中有一个非常关键的语句:_copyyStr = [copyyStr copy];
第三种场景中用self.copyyStr = originStr 赋值时,调用copyyStr的setter方法,setter方法对传入的copyyStr做了次深拷贝生成了一个新的对象赋值给_copyyStr,所以_copyyStr指向的地址和对象值都不再和originStr相同。
第四种:
NSString *originStr4 = [NSString stringWithFormat:@"hello,everyone"];
self.strongStr = originStr4;
self.copyyStr = originStr4;
NSLog(@"第三种场景:用NSMutableString点语法赋值");
NSLog(@" 对象地址 对象指针地址 对象的值 ");
NSLog(@"originStr: %p , %p , %@", originStr4, &originStr4, originStr4);
NSLog(@"strongStr: %p , %p , %@", _strongStr, &_strongStr, _strongStr);
NSLog(@" copyyStr: %p , %p , %@", _copyyStr, &_copyyStr, _copyyStr);
结果:  原因:这里的copy是浅拷贝,并没有生成新的对象。
总结:
由上面的例子可以得出:
- 当原字符串是NSString时,由于是不可变字符串,所以,不管使用strong还是copy修饰,都是指向原来的对象,copy操作只是做了一次浅拷贝。
- 而当源字符串是NSMutableString时,strong只是将源字符串的引用计数加1,而copy则是对原字符串做了次深拷贝,从而生成了一个新的对象,并且copy的对象指向这个新对象。另外需要注意的是,这个copy属性对象的类型始终是NSString,而不是NSMutableString,如果想让拷贝过来的对象是可变的,就要使用mutableCopy。所以,如果源字符串是NSMutableString的时候,使用strong只会增加引用计数。但是copy会执行一次深拷贝,会造成不必要的内存浪费。而如果原字符串是NSString时,strong和copy效果一样,就不会有这个问题。
但是,我们一般声明NSString时,也不希望它改变,所以一般情况下,建议使用copy,这样可以避免NSMutableString带来的错误。 3.如果property是不可变数据的时候,最好选择使用copy属性修饰。这是为了防止赋值给它的是可变的数据,如果可变的数据发生了变化,那么该property也会发生变化。
|