作为一个开发者,我们每天都在新的需求中创建一个又一个Object,每个类都可能会有一些私有或共有的属性,来满足我们的开发需求。
那么,每一个属性要如何来用关键字定义使用?每个修饰属性的关键字之间又有什么区别,你真的了解吗?
下面代码中的修饰属性的关键字,大家一定再熟悉不过了,如果你不熟悉,骚年您转行吧!
@interface ViewController ()
@property (nonatomic, assign) NSInteger persionAge;
@property (nonatomic, copy) NSString *persionName;
@property (nonatomic, weak) UIImageView *persionIconView;
@property (nonatomic, strong) NSArray *persionArray;
@end
下面Zombie代大家详细了解一下,assign、copy、weak、strong之间的同处、异处。
是否会开辟新的内存
是否会对地址引用计数增加
哪里讲的不好,欢迎大家留言指出、愿意和大家共同探讨!
一. 我们举一个典型的例子非容器可变变量'NSMutableString'
来了解一下。
@interface ViewController ()
@property (nonatomic, copy) NSString *strCopy;
@property (nonatomic, strong) NSString *strStrong;
@property (nonatomic, weak) NSString *strWeak;
@property (nonatomic, assign) NSString *strAssign;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSMutableString *testStr = [NSMutableString stringWithString:@"address"];
self.strCopy= testStr;
self.strStrong= testStr;
self.strWeak= testStr;
self.strAssign= testStr;
// 输出结果
NSLog(@"testStr 输出:%p, %@", testStr, testStr);
NSLog(@"strCopy 输出:%p, %@",_strCopy, _strCopy);
NSLog(@"strStrong 输出:%p, %@",_strStrong,_strStrong);
NSLog(@"strWeak 输出:%p, %@",_strWeak, _strWeak);
NSLog(@"strAssign 输出:%p, %@",_strAssign,_strAssign);
NSLog(@"引用计数为%@",[testStr valueForKey:@"retainCount"]);
// 输出内容
testStr 输出:0x60400005d0d0, address
strCopy 输出:0xa737365726464617, address
strStrong 输出:0x60400005d0d0, address
strWeak 输出:0x60400005d0d0, address
strAssign 输出:0x60400005d0d0, address
引用计数为2
}
以上结果我们看出
-
strStrong
、strWeak
、strAssign
指针指向的地址都与testStr
相同 -
copy
修饰的strCopy
,赋值后则是自己单独开辟了一块内存地址,内存上保存address
字符串,并指向它。 -
testStr
的引用计数为2,并不为4;strWeak
、strAssign
指针虽然都指向了testStr
的内存地址,但并不会使testStr
的引用计数增加。
根据以上结果我们可以看出,当我们现在修改了testStr的值,势必不会对strCopy的值造成影响,OK我们来测试一下。
[testStr stringByAppendingString:@"_xxoo"];
NSLog(@"testStr 输出:%p, %@", testStr, testStr);
NSLog(@"strCopy 输出:%p, %@",_strCopy, _strCopy);
NSLog(@"strStrong 输出:%p, %@",_strStrong, _strStrong);
NSLog(@"strWeak 输出:%p, %@",_strWeak, _strWeak);
NSLog(@"strAssign 输出:%p, %@",_strAssign, _strAssign);
// 输出内容
testStr 输出:0x60400005d0d0, address_xxoo
strCopy 输出:0xa737365726464617, address // 未发生变化
strStrong 输出:0x60400005d0d0, address_xxoo
strWeak 输出:0x60400005d0d0, address_xxoo
strAssign 输出:0x60400005d0d0, address_xxoo
- 使用
copy
会重新开辟一块新的内存来保存相同的数据,原内存内容被修改对它互相不影响。 -
strong
&&weak
都指向原来的内存地址,所以值也会被修改; -
strong
会使数据地址引用计数+1,但weak
不会。
那么引用计数+1,到底有什么实际上的不一样呢?
我们重新实例化testStr,然后来看下面一段代码
testStr = [[NSMutableString alloc] initWithString:@"abc"];
NSLog(@"引用计数%@",[testStr valueForKey:@"retainCount"]);
NSLog(@"testStr 输出:%p, %@", testStr, testStr);
NSLog(@"strCopy 输出:%p, %@",_strCopy, _strCopy);
NSLog(@"strStrong 输出:%p, %@",_strStrong, _strStrong);
NSLog(@"strWeak 输出:%p, %@",_strWeak, _strWeak);
NSLog(@"strAssign 输出:%p, %@",_strAssign, _strAssign);
// 输出内容
testStr 输出:0x608000055c90, abc
strCopy 输出:0xa737365726464617, address
strStrong 输出:0x60400044df50, address_xxoo
strWeak 输出:0x60400044df50, address_xxoo
strAssign 输出:0x60400044df50, address_xxoo
-
strCopy
我们看出,并没有受到任何影响 -
strStrong
、strWeak
、strAssign
不等于abc
,而等于testStr
之前的内容,为什么呢?这是因为strStrong
使testStr
所指向的原地址引用计数+1,所以testStr
重新实例化之后并没有使strWeak
、strAssign
的值为空。
我们在来看一段代码,再来讨论。
testStr = [[NSMutableString alloc] initWithString:@"abc"];
self.strStrong = nil;
NSLog(@"testStr 输出:%p, %@", testStr, testStr);
NSLog(@"strCopy 输出:%p, %@",_strCopy, _strCopy);
NSLog(@"strStrong 输出:%p, %@",_strStrong, _strStrong);
NSLog(@"strWeak 输出:%p, %@",_strWeak, _strWeak);
NSLog(@"strAssign 输出:%p, %@",_strAssign, _strAssign);
// 输出内容
testStr 输出:0x60c0002464e0, abc
strCopy 输出:0xa737365726464617, address
strStrong 输出:0x0, (null)
strWeak 输出:0x0, (null)
strAssign···崩溃···
- 当设置了
strStrong
为nil后,strStrong
指向testStr
原来的那一块内存地址引用就会-1,造成原内存地址释放,所以strWeak
为null了,strStrong
当然也为null。 - 当原内存地址释放后,指向原内存地址的
strAssign
产生程序崩溃,而strWeak
不会。后面我们在讲解这是什么原因。
我们称会对数据地址增加引用计数的为强引用,不改变引用计数的为弱引用。
二. 我们来看一下assign
&&weak
的区别。
先来看一段代码
NSMutableString *testString = [[NSMutableString alloc] initWithString:@"abc"];
self.strAssign = testString;
self.strWeak = testString;
testString = [[NSMutableString alloc] initWithString:@"test"];
NSLog(@"strWeak 输出:%p, %@",_strWeak, _strWeak);
NSLog(@"strAssign 输出:%p, %@",_strAssign, _strAssign);
// 输出内容
strWeak 输出:0x0, (null)
strAssign···崩溃···
-
assign
如果赋值为nil的时候,并不会使assign
指向的地址的数据抹除,只是对自己的值进行释放。 - 我们可以发现在输出
strAssign
的时候会出现崩溃的情况。
原因其实是出现了野指针的问题,strAssign
和strWeak
相同,指向testString
的内存地址,但引用计数不+1。
当testString
重新实例化后,原指向的内存地址引用计数为0会被释放,这个时候assign
指向的地址就不存在了,所以会发生崩溃;如果这块内存地址没有被其他内容重新占用时,assign
还可以正常输出内容,但是一旦被从新写入数据,该指针指向地址的内容随时可能没有值,或错乱值,更及其容易造成崩溃。
三. retain
是什么
- ARC之前属性构造器的关键字是
retain
、copy
、assign
。 -
strong
、weak
是ARC带出来的关键字。 -
retain
等同于strong
,指针指向值内存地址,同时进行引用计数加1。
四. 非NSMutableString的情况
- 我在后续 属性关键字-(进阶篇) 和大家一同讨论
本章小节
当我们使用strStrong
、strWeak
、strAssign
、copy
关键字来修饰我们定义的属性的时候,首先要明确每个关键字的含义,和它们之间的不同点,再来分配给我们的属性。
再我们赋值的时候,要想清楚我们的目的,修改属性内容的时候也要注意修改对其他的对象的值有无其他影响,这样才能使我们的程序更在稳定。