Copy&mutableCopy
- 通过copy方法可以创建可变或不可变对象的不可变副本,通过mutableCopy可以创建可变或不可变对象的可变副本。
- 拷贝分为浅拷贝和深拷贝:
- 浅拷贝:指针拷贝,对一个对象进行浅拷贝,相当于对指向该对象的指针进行复制,产生一个新的指向这个对象的指针。当一个对象销毁后,两个指针都应该置空。
- 深拷贝:内容拷贝,增加一个指针并且申请一个新的内存,使这个新增的指针指向这个新的内存。使用深拷贝不会出现浅拷贝时重复释放同一块内存的错误!
Not to say, show the code
一、NSArray
- 示例:不可变数组copy之后数组的地址没有变,是浅拷贝;mutableCopy之后数组的地址不同,但是里面的对象的地址相同,是深度为1的深拷贝。
- NSArray *array = @[[NSMutableString stringWithString:@"a"], @"b"];NSArray *arrCopy = [array copy];NSArray *arrMurableCopy = [array mutableCopy];NSLog(@"arr address: %p", array);NSLog(@"arrCopy address: %p",arrCopy);NSLog(@"arrMurableCopy address:%p", arrMurableCopy);NSLog(@"arr first address: %p", [array objectAtIndex:0]);NSLog(@"arrCopy first address: %p", [arrCopy objectAtIndex:0]);NSLog(@"arrMurableCopy first address:%p", [arrMurableCopy objectAtIndex:0]);[self myNslog:array];[self myNslog:arrCopy];[self myNslog:arrMurableCopy];[array[0] appendString:@"aaa"];NSLog(@"arr address: %p", array);NSLog(@"arrCopy address: %p",arrCopy);NSLog(@"arrMurableCopy address:%p", arrMurableCopy);NSLog(@"arr first address: %p", [array objectAtIndex:0]);NSLog(@"arrCopy first address: %p", [arrCopy objectAtIndex:0]);NSLog(@"arrMurableCopy first address:%p", [arrMurableCopy objectAtIndex:0]);[self myNslog:array];[self myNslog:arrCopy];[self myNslog:arrMurableCopy];
- arr address: 0x146e5a9f0arrCopy address: 0x146e5a9f0arrMurableCopy address: 0x146e3aa90arr first address: 0x146e5a9b0arrCopy first address: 0x146e5a9b0arrMurableCopy first address: 0x146e5a9b0( a, b)( a, b)( a, b)arr address: 0x146e5a9f0arrCopy address: 0x146e5a9f0arrMurableCopy address: 0x146e3aa90arr first address: 0x146e5a9b0arrCopy first address: 0x146e5a9b0arrMurableCopy first address: 0x146e5a9b0( aaaa, b)( aaaa, b)( aaaa, b)
- 不可变数组copy之后数组的地址没有变,是浅拷贝;mutableCopy之后数组的地址不同,但是里面的对象的地址相同,是深度为1的深拷贝。
- 从打印结果可以看出,打印的数组的第一个对象的地址相同,可见里面存放的是对象的内存地址, 并没有开辟内存空间存放对象。所以当为一个数组的第一个对象附加aaa后,所有数组的第一个对象值都变为aaaa
二、NSMutableArray
- 示例一:从结果可见,可变数组copy 和 mutableCopy之后数组的地址都不同,然而数组的第一个对象的地址都相同,说明没有开辟新的地址存放数组里面的对象,说明进行了深度为1的深拷贝,数组里面存的是 a , b 对象组成的数组的首地址。改变一个数组的第一个值,其他数组也发生变化。
- // 可变数组NSMutableArray *mutableArr = [NSMutableArray arrayWithArray:@[[NSMutableString stringWithString:@"a"], @"b"]];NSMutableArray *mutableArrCopy = [mutableArr copy];NSMutableArray *mutableArrMutableCopy = [mutableArr mutableCopy];NSLog(@"mutableArr address: %p", mutableArr);NSLog(@"mutableArrCopy address: %p", mutableArrCopy);NSLog(@"mutableArrMutableCopy address:%p", mutableArrMutableCopy);NSLog(@"mutableArr first address: %p", [mutableArr objectAtIndex:0]);NSLog(@"mutableArrCopy first address: %p", [mutableArrCopy objectAtIndex:0]);NSLog(@"mutableArrMutableCopy first address:%p", [mutableArrMutableCopy objectAtIndex:0]);[self myNslog:mutableArr];[self myNslog:mutableArrCopy];[self myNslog:mutableArrMutableCopy];// 可变数组改变第一个值[mutableArr[0] appendString:@"aaa"];NSLog(@"mutableArr first address: %p", [mutableArr objectAtIndex:0]);NSLog(@"mutableArrCopy first address: %p", [mutableArrCopy objectAtIndex:0]);NSLog(@"mutableArrMutableCopy first address:%p", [mutableArrMutableCopy objectAtIndex:0]);[self myNslog:mutableArr];[self myNslog:mutableArrCopy];[self myNslog:mutableArrMutableCopy];
- mutableArr address: 0x135e2e040mutableArrCopy address: 0x135e19880mutableArrMutableCopy address: 0x135d0f0a0mutableArr first address: 0x135d49580mutableArrCopy first address: 0x135d49580mutableArrMutableCopy first address:0x135d49580( a, b)( a, b)( a, b)mutableArr first address: 0x135d49580mutableArrCopy first address: 0x135d49580mutableArrMutableCopy first address:0x135d49580( aaaa, b)( aaaa, b)( aaaa, b)
- 从结果可见,可变数组copy 和 mutableCopy之后数组的地址都不同,然而数组的第一个对象的地址都相同,说明没有开辟新的地址存放数组里面的对象,说明进行了深度为1的深拷贝,数组里面存的是 a , b 对象组成的数组的首地址。改变一个数组的第一个值,其他数组也发生变化。
- 示例二:这里创建一个新的可变字符串对象,并赋值给 mutableArr[0],而其他数组指针指向的对象不变,所以输出时其他数组值不变。
- // 设置新值mutableArr[0] = [NSMutableString stringWithString:@"A"];NSLog(@"mutableArr first address:%p", [mutableArr objectAtIndex:0]);NSLog(@"mutableArrCopy first address:%p", [mutableArrCopy objectAtIndex:0]);NSLog(@"mutableArrMutableCopy first address:%p", [mutableArrMutableCopy objectAtIndex:0]);[self myNslog:mutableArr];[self myNslog:mutableArrCopy];[self myNslog:mutableArrMutableCopy];
- 设置新值之后的打印;
- mutableArr first address: 0x12f625590mutableArrCopy first address: 0x135d49580mutableArrMutableCopy first address:0x135d49580( A, b)( A, b)( A, b)
- 这里创建一个新的可变字符串对象,并赋值给 mutableArr[0],而其他数组指针指向的对象不变,所以输出时其他数组值不变。
- 示例三:这里创建了一个新的可变字符串对象,并将其内存地址保存到指针mutableArr[3]中。而其他几个数组指针的引用不受影响。
- // 添加新值[mutableArr addObject:[NSMutableString stringWithString:@"c"]];NSLog(@"mutableArr first address: %p", [mutableArr objectAtIndex:0]);NSLog(@"mutableArrCopy first address: %p", [mutableArrCopy objectAtIndex:0]);NSLog(@"mutableArrMutableCopy first address:%p", [mutableArrMutableCopy objectAtIndex:0]);[self myNslog:mutableArr];[self myNslog:mutableArrCopy];[self myNslog:mutableArrMutableCopy];
- mutableArr first address: 0x135d49580mutableArrCopy first address: 0x135d49580mutableArrMutableCopy first address:0x135d49580( a, b, c)( a, b)( a, b)
- 这里创建了一个新的可变字符串对象,并将其内存地址保存到指针mutableArr[3]中。而其他几个数组指针的引用不受影响
- 示例四:操作的是mutableArr对对象的引用,所以不会影响到其他数组
- // 操作数组的引用NSMutableString *mutableString = [NSMutableString stringWithString:mutableArr[0]];[mutableString appendString:@"aaa"];[mutableArr replaceObjectAtIndex:0 withObject:mutableString];NSLog(@"mutableArr first address: %p", [mutableArr objectAtIndex:0]);NSLog(@"mutableArrCopy first address: %p", [mutableArrCopy objectAtIndex:0]);NSLog(@"mutableArrMutableCopy first address:%p", [mutableArrMutableCopy objectAtIndex:0]);[self myNslog:mutableArr];[self myNslog:mutableArrCopy];[self myNslog:mutableArrMutableCopy];
- mutableArr first address: 0x135d49570mutableArrCopy first address: 0x135d49580mutableArrMutableCopy first address:0x135d49580( aaaa, b, c)( a, b)( a, b)
- // 操作数组的引用[mutableArr removeObjectAtIndex:1];NSLog(@"mutableArr first address: %p", [mutableArr objectAtIndex:0]);NSLog(@"mutableArrCopy first address: %p", [mutableArrCopy objectAtIndex:0]);NSLog(@"mutableArrMutableCopy first address:%p", [mutableArrMutableCopy objectAtIndex:0]);[self myNslog:mutableArr];[self myNslog:mutableArrCopy];[self myNslog:mutableArrMutableCopy];
- mutableArr first address: 0x135d49580mutableArrCopy first address: 0x135d49580mutableArrMutableCopy first address:0x135d49580( a)( a, b)( a, b)
- 操作的是mutableArr对对象的引用,所以不会影响到其他数组
三、NSString
- 示例:copy 之后地址没有变,mutableCopy之后地址发生了变化,当改变了stringMutableCopy字符串之后,其他连个字符串没有发生变化,说明NSString copy 是浅拷贝,mutableCopy是深拷贝
- NSString *string = @"abc";NSString *stringCopy = [string copy];NSMutableString *stringMutableCopy = [string mutableCopy];NSLog(@"string = %p", string);NSLog(@"stringCopy = %p", stringCopy);NSLog(@"stringMutableCopy = %p", stringMutableCopy);[stringMutableCopy appendString:@"def"];NSLog(@"string = %p", string);NSLog(@"stringCopy = %p", stringCopy);NSLog(@"stringMutableCopy = %p", stringMutableCopy);NSLog(@"string = %@", string);NSLog(@"stringCopy = %@", stringCopy);NSLog(@"stringMutableCopy = %@", stringMutableCopy);
- string = 0x1000f0310stringCopy = 0x1000f0310stringMutableCopy = 0x14752fcf0string = 0x1000f0310stringCopy = 0x1000f0310stringMutableCopy = 0x14752fcf0string = abcstringCopy = abcstringMutableCopy = abcdef
- copy 之后地址没有变,mutableCopy之后地址发生了变化,当改变了stringMutableCopy字符串之后,其他连个字符串没有发生变化,说明NSString copy 是浅拷贝,mutableCopy是深拷贝
四、NSMutableString
- 示例:
- NSMutableString *mutaleString = [NSMutableString stringWithString:@"a"];NSMutableString *mutaleStringcopy = [mutaleString copy];NSMutableString *mutaleStringmutableCopy = [mutaleString mutableCopy];NSLog(@"mutaleString = %p", mutaleString);NSLog(@"mutaleStringcopy = %p", mutaleStringcopy);NSLog(@"mutaleStringmutableCopy = %p", mutaleStringmutableCopy);[mutaleString appendString:@"bcd"];NSLog(@"mutaleString = %p", mutaleString);NSLog(@"mutaleStringcopy = %p", mutaleStringcopy);NSLog(@"mutaleStringmutableCopy = %p", mutaleStringmutableCopy);NSLog(@"mutaleString = %@", mutaleString);NSLog(@"mutaleStringcopy = %@", mutaleStringcopy);NSLog(@"mutaleStringmutableCopy = %@", mutaleStringmutableCopy);
- mutaleString = 0x14e637470mutaleStringcopy = 0xa000000000000611mutaleStringmutableCopy = 0x14e6374b0mutaleString = 0x14e637470mutaleStringcopy = 0xa000000000000611mutaleStringmutableCopy = 0x14e6374b0mutaleString = abcdmutaleStringcopy = amutaleStringmutableCopy = a
- mutaleString = [NSMutableStringstringWithString:@"A"];NSLog(@"mutaleString = %p", mutaleString);NSLog(@"mutaleStringcopy = %p", mutaleStringcopy);NSLog(@"mutaleStringmutableCopy = %p", mutaleStringmutableCopy);NSLog(@"mutaleString = %@", mutaleString);NSLog(@"mutaleStringcopy = %@", mutaleStringcopy);NSLog(@"mutaleStringmutableCopy = %@", mutaleStringmutableCopy);
- mutaleString = 0x14e637470mutaleStringcopy = 0xa000000000000611mutaleStringmutableCopy = 0x14e6374b0mutaleString = 0x13c657000mutaleStringcopy = 0xa000000000000611mutaleStringmutableCopy = 0x14e6374b0mutaleString = AmutaleStringcopy = amutaleStringmutableCopy = a
- copy 和 mutableCopy都进行了深拷贝,开辟了 新的地址空间存储 拷贝后的变量;
- 注意:copy 后的地址 0xa000000000000611 跟其他的不一样,copy后的对象是不可变的,强行修改会导致程序崩溃。
- -[NSTaggedPointerString appendString:]: unrecognized selector sent to instance 0xa000000000000611*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSTaggedPointerString appendString:]: unrecognized selector sent to instance 0xa000000000000611'
五、NSMutableDictionary
- 示例一:mutableDict 的索引key1, key2, key3 分别对应对象 mutableStr1, mutableStr2, mutableStr3 的内存地址,copy 后所有的字典第一个对象的地址都相同,对字典拷贝也是浅拷贝。
- NSMutableString *mutableStr1 = [NSMutableString stringWithString:@"a"];NSMutableString *mutableStr2 = [NSMutableString stringWithString:@"b"];NSMutableString *mutableStr3 = [NSMutableString stringWithString:@"c"];NSMutableDictionary *mutableDict = [NSMutableDictionary dictionaryWithObjects:@[mutableStr1, mutableStr2, mutableStr3] forKeys:@[@"key1", @"key2", @"key3"]];NSMutableDictionary *mutableDictCopy = [mutableDict copy];NSMutableDictionary *mutableDictMutableCopy = [mutableDict mutableCopy];NSLog(@"mutableDict = %p", mutableDict);NSLog(@"mutableDictCopy = %p", mutableDictCopy);NSLog(@"mutableDictMutableCopy = %p", mutableDictMutableCopy);NSLog(@"mutableDict first address = %p", mutableDict[@"key1"]);NSLog(@"mutableDictCopy first address = %p", mutableDictCopy[@"key1"]);NSLog(@"mutableDictMutableCopy first address = %p", mutableDictMutableCopy[@"key1"]);[mutableDict[@"key1"] appendString:@"aaa"];NSLog(@"mutableDict = %@", mutableDict);NSLog(@"mutableDictCopy = %@", mutableDictCopy);NSLog(@"mutableDictMutableCopy = %@", mutableDictMutableCopy);NSLog(@"mutableDict first address = %p", mutableDict[@"key1"]);NSLog(@"mutableDictCopy first address = %p", mutableDictCopy[@"key1"]);NSLog(@"mutableDictMutableCopy first address = %p", mutableDictMutableCopy[@"key1"]);
- mutableDict = 0x15f62f7d0mutableDictCopy = 0x15f62f800mutableDictMutableCopy = 0x15f62f570mutableDict first address = 0x15f62f530mutableDictCopy first address = 0x15f62f530mutableDictMutableCopy first address = 0x15f62f530mutableDict = { key1 = aaaa; key2 = b; key3 = c;}mutableDictCopy = { key1 = aaaa; key2 = b; key3 = c;}mutableDictMutableCopy = { key1 = aaaa; key2 = b; key3 = c;}mutableDict first address = 0x15f62f530mutableDictCopy first address = 0x15f62f530mutableDictMutableCopy first address = 0x15f62f530
- mutableDict 的索引key1, key2, key3 分别对应对象 mutableStr1, mutableStr2, mutableStr3 的内存地址,copy 后所有的字典第一个对象的地址都相同,对字典拷贝也是浅拷贝。
- 对key1对应的对象mutableStr1进行修改后,由于指向同一个字典,所以输出相同,第一个对象的地址也相同。
- 示例二:
- [mutableDict setObject:@"A" forKey:@"key1"];NSLog(@"mutableDict = %@", mutableDict);NSLog(@"mutableDictCopy = %@", mutableDictCopy);NSLog(@"mutableDictMutableCopy = %@", mutableDictMutableCopy);NSLog(@"mutableDict first address = %p", mutableDict[@"key1"]);NSLog(@"mutableDictCopy first address = %p", mutableDictCopy[@"key1"]);NSLog(@"mutableDictMutableCopy first address = %p", mutableDictMutableCopy[@"key1"]);
- mutableDict = { key1 = A; key2 = b; key3 = c;}mutableDictCopy = { key1 = a; key2 = b; key3 = c;}mutableDictMutableCopy = { key1 = a; key2 = b; key3 = c;}mutableDict first address = 0x1000fc390mutableDictCopy first address = 0x15f62f530mutableDictMutableCopy first address = 0x15f62f530
- mutableDict中key1存放的是第一个对象的内存地址,[mutableDict setObject:@"A" forKey:@"key1”]; 将key1中的内存地址修改为 @"A"的内存地址,不会影响到其他引用所指向的对象。
- 示例三:为 mutableDict 添加先得键值对,没有影响到其他字典指针,所以只有mutableDict发生变化。
- [mutableDict setObject:@"d" forKey:@"key4"];NSLog(@"mutableDict = %@", mutableDict);NSLog(@"mutableDictCopy = %@", mutableDictCopy);NSLog(@"mutableDictMutableCopy = %@", mutableDictMutableCopy);NSLog(@"mutableDict first address = %p", mutableDict[@"key1"]);NSLog(@"mutableDictCopy first address = %p", mutableDictCopy[@"key1"]);NSLog(@"mutableDictMutableCopy first address = %p", mutableDictMutableCopy[@"key1"]);
- mutableDict = { key1 = A; key2 = b; key3 = c; key4 = d;}mutableDictCopy = { key1 = a; key2 = b; key3 = c;}mutableDictMutableCopy = { key1 = a; key2 = b; key3 = c;}mutableDict first address = 0x15f62f530mutableDictCopy first address = 0x15f62f530mutableDictMutableCopy first address = 0x15f62f530
- 为 mutableDict 添加先得键值对,没有影响到其他字典指针,所以只有mutableDict发生变化。
总结:NSMutableArray和NSMutableDictionary的复制是浅复制,NSMutableString的复制是深复制。由于数组或字典中对象数目或大小可能非常大,所以对对象的复制可能引起大量开销,因此这里只复制引用可以节省开销。