上一篇文章基础篇,我们用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
内的元素没有增加,因为他是新开辟的内存创建的容器,容器内只有原来两个元素并指向两个元素的地址。 -
arrayStrong
、arrayWeak
都是直接指向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 )
容器可变变量总结
- 修改原数组中的元素后,我们看到
arrayStrong
、arrayWeak
、arrayCopy
内部的元素都发生了变化, - 这说明"容器可变变量
中的元素
"在拷贝过程中无论Copy
、Weak
、Strong
都是浅拷贝。 - 但"容器可变变量的
容器本身
"只有Copy
是深拷贝,其余Weak
、Strong
都是浅拷贝。
二. 非容器不变变量
@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
}
- 赋值后
stringCopy
、stringStrong
、stringWeak
的指针都指向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)
- 将
stringCopy
,stringStrong
设为nil后,发现stringWeak
随之改为nil。 - 因为当
testStr
重新赋值后,原来指向的内存地址引用计数变为3-1=2
,当stringCopy
、stringStrong
置空后引用计数为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
。 -
Copy
、Strong
、Weak
、Assign
都是浅拷贝都不会增加容器内部元素的引用计数。
以下为全篇总结:
Copy
、Strong
、Assgin
、Weak
。
可变变量中:
-
Copy
会开辟一块新的内存;而Strong
、Assgin
、Weak
不会,他们只是将指针指向保存值的内存对应的地址; -
Strong
会再指向地址后对该内存引用计数+1; -
Weak
、Assgin
则不会增加引用计数,但会在指向的地址引用计数为0
时将值置为空,并且Weak
会将内存置为nil
,而Assgin
不会,Assgin
会在内存被重写之前继续输出,一旦内存被重写后会引起程序崩溃。
不可变变量中:
变量本身不可修改,Copy
没有必要开辟一块新的内存存放一摸一样的内容,所以默认为浅拷贝。
Strong
、Assgin
、Weak
则和可变变量保持一致。
容器:
容器本身遵守上面可变与不可变
的原则,只是内部元素
只会被浅拷贝
。
网友评论