属性关键字-(基础篇)

作为一个开发者,我们每天都在新的需求中创建一个又一个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
}
以上结果我们看出
  • strStrongstrWeakstrAssign指针指向的地址都与testStr相同
  • copy修饰的strCopy,赋值后则是自己单独开辟了一块内存地址,内存上保存address字符串,并指向它。
  • testStr的引用计数为2,并不为4;strWeakstrAssign指针虽然都指向了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我们看出,并没有受到任何影响
  • strStrongstrWeakstrAssign不等于abc,而等于testStr之前的内容,为什么呢?这是因为strStrong使testStr所指向的原地址引用计数+1,所以testStr重新实例化之后并没有使strWeakstrAssign的值为空。

我们在来看一段代码,再来讨论。

    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的时候会出现崩溃的情况。
    原因其实是出现了野指针的问题,strAssignstrWeak相同,指向testString的内存地址,但引用计数不+1。
    testString重新实例化后,原指向的内存地址引用计数为0会被释放,这个时候assign指向的地址就不存在了,所以会发生崩溃;如果这块内存地址没有被其他内容重新占用时,assign还可以正常输出内容,但是一旦被从新写入数据,该指针指向地址的内容随时可能没有值,或错乱值,更及其容易造成崩溃。

三. retain是什么

  • ARC之前属性构造器的关键字是retaincopyassign
  • strongweak是ARC带出来的关键字。
  • retain等同于strong,指针指向值内存地址,同时进行引用计数加1。

四. 非NSMutableString的情况

  • 我在后续 属性关键字-(进阶篇) 和大家一同讨论

本章小节
当我们使用strStrongstrWeakstrAssigncopy关键字来修饰我们定义的属性的时候,首先要明确每个关键字的含义,和它们之间的不同点,再来分配给我们的属性。
再我们赋值的时候,要想清楚我们的目的,修改属性内容的时候也要注意修改对其他的对象的值有无其他影响,这样才能使我们的程序更在稳定。

你可能感兴趣的:(属性关键字-(基础篇))