【iOS开发】——属性关键字

在定义属性的时候,都需要为属性加特性,属性的特性决定了属性在原子性、读写权限以及内存管理的特性。

第一类:原子性(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

当采用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直接赋值
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点语法赋值
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点语法赋值
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是浅拷贝,并没有生成新的对象。

总结:

由上面的例子可以得出:

  1. 当原字符串是NSString时,由于是不可变字符串,所以,不管使用strong还是copy修饰,都是指向原来的对象,copy操作只是做了一次浅拷贝。
  2. 而当源字符串是NSMutableString时,strong只是将源字符串的引用计数加1,而copy则是对原字符串做了次深拷贝,从而生成了一个新的对象,并且copy的对象指向这个新对象。另外需要注意的是,这个copy属性对象的类型始终是NSString,而不是NSMutableString,如果想让拷贝过来的对象是可变的,就要使用mutableCopy。所以,如果源字符串是NSMutableString的时候,使用strong只会增加引用计数。但是copy会执行一次深拷贝,会造成不必要的内存浪费。而如果原字符串是NSString时,strong和copy效果一样,就不会有这个问题。
    但是,我们一般声明NSString时,也不希望它改变,所以一般情况下,建议使用copy,这样可以避免NSMutableString带来的错误。
    3.如果property是不可变数据的时候,最好选择使用copy属性修饰。这是为了防止赋值给它的是可变的数据,如果可变的数据发生了变化,那么该property也会发生变化。

Guess you like

Origin blog.csdn.net/weixin_50990189/article/details/119449019
Recommended