属性关键字-(进阶篇)

上一篇文章基础篇,我们用NSMutableString为例详细讲解了“非容器可变变量”。相信大家都已经基本掌握了重点在不同关键字的定义下,它们有何相同以及不同之处。
但通过上一篇我们只了解了主题的一半内容而已,接下来内容可能有点多,不过知识点和内容与上一篇大同小异。
如果你有仔细学习基础篇,那么进阶篇学起来会Very Easy。

一. 今天我们先用NSMutableArray举例来讲解容器可变变量

@property(nonatomic, copy)  NSMutableArray *arrayCopy;
@property(nonatomic, strong)NSMutableArray *arrayStrong;
@property(nonatomic, weak)  NSMutableArray *arrayWeak;

- (void)viewDidLoad {
    
    [super viewDidLoad];
    NSMutableArray *testArray = [[NSMutableArray alloc] init];
    NSMutableString *str1 = [[NSMutableString alloc]initWithString:@"test1"];
    NSMutableString *str2 = [[NSMutableString alloc]initWithString:@"test2"];
    
    [testArray addObject:str1];
    [testArray addObject:str2];
    
    self.arrayCopy= testArray;
    self.arrayStrong= testArray;
    self.arrayWeak= testArray;
    
    NSLog(@"testArray   输出:%p, %@", testArray,testArray);
    NSLog(@"arrayCopy   输出:%p, %@",_arrayCopy,_arrayCopy);
    NSLog(@"arrayStrong 输出:%p, %@",_arrayStrong,_arrayStrong);
    NSLog(@"arrayWeak   输出:%p, %@",_arrayWeak,_arrayWeak);
    NSLog(@"arrayWeak   输出:%p, %@",_arrayWeak[0],_arrayWeak[0]);
    NSLog(@"testArray中的数据引用计数%@", [testArray valueForKey:@"retainCount"]);
    // 输出内容
    testArray   输出:0x604000441470, ( test1, test2 )
    arrayCopy   输出:0x60400022a140, ( test1, test2 )
    arrayStrong 输出:0x604000441470, ( test1, test2 )
    arrayWeak   输出:0x604000441470, ( test1, test2 )
    arrayWeak   输出:0x604000441470, test1
    testArray中的数据引用计数( 3, 3 )
}
  • 这里只有arrayCopy的地址发生了变化,说明arrayCopy为深拷贝,它会重新开辟一块内存来创建一块新容器。但新的容器内copy来的两个元素指为浅拷贝,指针的指向还是str1、str2,这样就会使str1、str2的地址的引用计数+1。
  • arrayWeak、arrayStrong都是浅拷贝、不会开辟新的内存,也不会使容器内部的元素引用计数增加。
接下来我们来给测试数组添加一个元素
NSMutableString *str3 = [[NSMutableString alloc]initWithString:@"test3"];
    [testArray addObject:str3];
    NSLog(@"testArray   输出:%p, %@", testArray,testArray);
    NSLog(@"arrayCopy   输出:%p, %@",_arrayCopy,_arrayCopy);
    NSLog(@"arrayStrong 输出:%p, %@",_arrayStrong,_arrayStrong);
    NSLog(@"arrayWeak   输出:%p, %@",_arrayWeak,_arrayWeak);
    NSLog(@"testArray中的数据引用计数%@", [testArray valueForKey:@"retainCount"]);
    // 输出内容
    testArray    输出:0x604000441470, ( test1, test2, test3 )
    arrayCopy    输出:0x60400022a140, ( test1, test2 )
    arrayStrong  输出:0x604000441470, ( test1, test2, test3 )
    arrayWeak    输出:0x604000441470, ( test1, test2, test3 )
    testArray中的数据引用计数(3, 3, 2)
从结果可以看出
  • 只有arrayCopy内的元素没有增加,因为他是新开辟的内存创建的容器,容器内只有原来两个元素并指向两个元素的地址。
  • arrayStrongarrayWeak都是直接指向testArray的内存地址,所以也会随着testArray的改变而改变。
我们最后来修改原数组中的元素,看是否其他数组会随之变化
    [str1 appendFormat:@"abc"];
    
    NSLog(@"testArray   输出:%p, %@", testArray,testArray);
    NSLog(@"arrayCopy   输出:%p, %@",_arrayCopy,_arrayCopy);
    NSLog(@"arrayStrong 输出:%p, %@",_arrayStrong,_arrayStrong);
    NSLog(@"arrayWeak   输出:%p, %@",_arrayWeak,_arrayWeak);

    // 输出内容
     testArray   输出:0x604000441470, ( test1abc, test2, test3 )
     arrayCopy   输出:0x60400022a140, ( test1abc, test2 )
     arrayStrong 输出:0x604000441470, ( test1abc, test2, test3 )
     arrayWeak   输出:0x604000441470, ( test1abc, test2, test3 )

容器可变变量总结

  • 修改原数组中的元素后,我们看到arrayStrongarrayWeakarrayCopy内部的元素都发生了变化,
  • 这说明"容器可变变量中的元素"在拷贝过程中无论CopyWeakStrong都是浅拷贝。
  • 但"容器可变变量的容器本身"只有Copy是深拷贝,其余WeakStrong都是浅拷贝。
二. 非容器不变变量
@property (nonatomic, copy)   NSString *stringCopy;
@property (nonatomic, strong) NSString *stringStrong;
@property (nonatomic, weak)   NSString *stringWeak;
@property (nonatomic, assign) NSString *stringAssign;

- (void)p_TestCode
{
    NSString *testStr = [[NSString alloc] initWithUTF8String:"testCode123456"];

    self.stringCopy = testStr;
    self.stringWeak = testStr;
    self.stringStrong = testStr;
    self.stringAssign = testStr;

    NSLog(@"testStr     输出:%p,%@", testStr,testStr);
    NSLog(@"stringCopy  输出:%p,%@",_stringCopy,_stringCopy);
    NSLog(@"stringStrong输出:%p,%@",_stringStrong,_stringStrong);
    NSLog(@"stringWeak  输出:%p,%@",_stringWeak,_stringWeak);
    NSLog(@"testStr中的数据引用计数%@", [testStr valueForKey:@"retainCount"]);

    // 输出内容
    testStr      输出:0xa001003046c32808,testCode
    stringCopy   输出:0xa001003046c32808,testCode
    stringStrong 输出:0xa001003046c32808,testCode
    stringWeak   输出:0xa001003046c32808,testCode
}
  • 赋值后stringCopystringStrongstringWeak的指针都指向testStr的地址。
  • 此时我们testStr的引用计数应该为3,copy为浅拷贝,不开辟新的存储空间,但指向的内存地址引用计数+1。
接下来我们给testStr重新赋值,来看下面的代码
testStr = @"abc";

NSLog(@"testStr     输出:%p,%@", testStr,testStr);
NSLog(@"stringCopy  输出:%p,%@",_stringCopy,_stringCopy);
NSLog(@"stringStrong输出:%p,%@",_stringStrong,_stringStrong);
NSLog(@"stringWeak  输出:%p,%@",_stringWeak,_stringWeak);
NSLog(@"testStr     引用计数%@", [testStr valueForKey:@"retainCount"]);

 // 输出内容
 testStr      输出:0x103806110,aaa
 stringCopy   输出:0xa001003046c32808,testCode
 stringStrong 输出:0xa001003046c32808,testCode
 stringWeak   输出:0xa001003046c32808,testCode
  • stringCopy、stringStrong、stringWeak指向的地址和值仍然是testStr重新赋值之前的。
  • 细心的同学可能会发现当我们重新给testStr赋值时,testStr就指向了一块新的存储空间,但是原有内存地址引用计数为3,-1后扔为2,并不能释放掉这块内存。所以就解释了为什么testStr已经不指向原有内存地址了,但并没有影响其他成员的值
我们再来看下面的代码
self.stringCopy=nil;
self.stringStrong=nil;

NSLog(@"testStr     输出:%p,%@", testStr,testStr);
NSLog(@"stringCopy  输出:%p,%@",_stringCopy,_stringCopy);
NSLog(@"stringStrong输出:%p,%@",_stringStrong,_stringStrong);
NSLog(@"stringWeak  输出:%p,%@",_stringWeak,_stringWeak);
NSLog(@"testStr     引用计数%@", [testStr valueForKey:@"retainCount"]);

 // 输出内容
 testStr     输出:0x103806110,aaa
 stringCopy  输出:0x0,(null)
 stringStrong输出:0x0,(null)
 stringWeak  输出:0x0,(null)
  • stringCopystringStrong设为nil后,发现stringWeak随之改为nil。
  • 因为当testStr重新赋值后,原来指向的内存地址引用计数变为3-1=2,当stringCopystringStrong置空后引用计数为2-2=0。所以weak自然也是null
非容器不变变量总结
  • 不难看出NSString(非容器不可变变量) 和NSMutableString(非容器可变变量) 基本相同。
  • 除了copy不可变变量为浅拷贝,可变变量为深拷贝。那为什么copy不可变变量不自己开辟一个独立的内存出来呢?
  • 原因很简单,因为不可变变量的值不会改变所以系统觉得没必要再开辟一个内存出来让stringCopy指向它,直接指向原来的内存地址就可以了。

三. 不可变容器变量举例NSArray

@property(nonatomic, copy)  NSArray *arrayCopy;
@property(nonatomic, strong)NSArray *arrayStrong;
@property(nonatomic, weak)  NSArray *arrayWeak;

NSString *str1 = [[NSMutableString alloc]initWithString:@"test1"];
NSString *str2 = [[NSMutableString alloc]initWithString:@"test2"];

NSArray *testArray = [[NSArray alloc] initWithObjects:str1,str2, nil];

self.arrayCopy= testArray;
self.arrayStrong= testArray;
self.arrayWeak= testArray;

NSLog(@"testArray   输出:%p, %@", testArray,testArray);
NSLog(@"arrayCopy   输出:%p, %@",_arrayCopy,_arrayCopy);
NSLog(@"arrayStrong 输出:%p, %@",_arrayStrong,_arrayStrong);
NSLog(@"arrayWeak   输出:%p, %@",_arrayWeak,_arrayWeak);
NSLog(@"testArray中的数据引用计数%@", [testArray valueForKey:@"retainCount"]);
// 输出内容
testArray   输出:0x60400042a460, ( test1, test2 )
arrayCopy   输出:0x60400042a460, ( test1, test2 )
arrayStrong 输出:0x60400042a460, ( test1, test2 )
arrayWeak   输出:0x60400042a460, ( test1, test2 )
testArray中的数据引用计数( 2, 2 )
  • 在不可变容器变量中,容器本身都是浅拷贝包括copy,同NSString
  • CopyStrongWeakAssign都是浅拷贝都不会增加容器内部元素的引用计数

以下为全篇总结:CopyStrongAssginWeak

可变变量中:
  • Copy会开辟一块新的内存;而StrongAssginWeak不会,他们只是将指针指向保存值的内存对应的地址
  • Strong会再指向地址后对该内存引用计数+1
  • WeakAssgin不会增加引用计数,但会在指向的地址引用计数为0时将值置为空,并且Weak会将内存置为nil,而Assgin不会,Assgin会在内存被重写之前继续输出,一旦内存被重写后会引起程序崩溃
不可变变量中:

变量本身不可修改Copy没有必要开辟一块新的内存存放一摸一样的内容,所以默认为浅拷贝
StrongAssginWeak则和可变变量保持一致

容器:

容器本身遵守上面可变与不可变的原则,只是内部元素只会被浅拷贝


关于属性关键字Zombie就先聊到这.欢迎大家一起讨论.

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