在iOS開(kāi)發(fā)過(guò)程中据某,我們要對(duì)某個(gè)對(duì)象進(jìn)行拷貝的時(shí)候墩朦,一般會(huì)用copy
和mutableCopy
兩種方法勺阐,在剛接觸iOS的時(shí)候,我一般把copy
稱為淺拷貝幢痘,mutableCopy
稱為深拷貝唬格,但是隨著不斷踩坑家破,才發(fā)現(xiàn)對(duì)不同類型的對(duì)象執(zhí)行拷貝操作颜说,得到的結(jié)果跟自己的預(yù)期并不一致购岗。然后看了網(wǎng)上諸多關(guān)于深淺拷貝的文章,再結(jié)合自己寫(xiě)代碼測(cè)試门粪,才慢慢理解到喊积,其實(shí)iOS中的深淺拷貝得到的結(jié)果,是跟拷貝對(duì)象的類型有關(guān)系的玄妈。
在iOS開(kāi)發(fā)中乾吻,一般情況下,拷貝對(duì)象大概分為3種:非集合對(duì)象拟蜻、集合對(duì)象绎签、自定義對(duì)象。好了酝锅,話不多講诡必,以下就是針對(duì)這三種不同類型對(duì)象所寫(xiě)的深淺拷貝測(cè)試代碼。
1搔扁、iOS中非集合類的深淺拷貝
非集合類對(duì)象深淺拷貝示例代碼:
- (void)stringCopy{
NSString *str = @"123456";
// 內(nèi)容內(nèi)存地址不變爸舒,共用同一塊內(nèi)存
NSString *cpyStr = [str copy];
// 內(nèi)容地址改變,另外開(kāi)辟一塊內(nèi)存空間稿蹲,內(nèi)容可變
NSMutableString *mutCpyStr = [str mutableCopy];
// str = 0x10e6d9068; copyStr = 0x10e6d9068; mutStr = 0x604000253f20;
NSLog(@"NSString內(nèi)容地址:str = %p; copyStr = %p; mutStr = %p;",str,cpyStr,mutCpyStr);
// str = 0x7fff51525be8; copyStr = 0x7fff51525be0; mutStr = 0x7fff51525bd8;
NSLog(@"NSstring指針地址:str = %p; copyStr = %p; mutStr = %p;",&str,&cpyStr,&mutCpyStr);
}
- (void)mutableStringCopy{
NSMutableString *formatStr = [NSMutableString stringWithFormat:@"呵呵噠??"];
// 內(nèi)容地址改變扭勉,開(kāi)辟一塊內(nèi)存空間,內(nèi)容不可變
NSString *cpyStr = [formatStr copy];
// 內(nèi)容地址改變苛聘,開(kāi)辟一塊內(nèi)存空間涂炎,內(nèi)容可變
NSMutableString *mutCpyStr = [formatStr mutableCopy];
// str = 0x600000249690; copyStr = 0x600000249cc0; mutStr = 0x600000249750;
NSLog(@"NSMutableString內(nèi)容地址:str = %p; copyStr = %p; mutStr = %p;",formatStr,cpyStr,mutCpyStr);
// str = 0x7fff51525be8; copyStr = 0x7fff51525be0; mutStr = 0x7fff51525bd8;
NSLog(@"NSMutableString指針地址:str = %p; copyStr = %p; mutStr = %p;",&formatStr,&cpyStr,&mutCpyStr);
}
由上面的代碼可以看出,非集合對(duì)象的拷貝規(guī)律是這樣的:
- 不可變的非集合對(duì)象copy设哗,指針拷貝璧尸,共用同一片內(nèi)存;
- 不可變的非集合對(duì)象mutableCopy熬拒,創(chuàng)建新內(nèi)存空間爷光,成為可變對(duì)象;
- 可變的非集合對(duì)象copy澎粟,創(chuàng)建新內(nèi)存空間蛀序,成為不可變對(duì)象;
- 可變的非集合對(duì)象mutableCopy活烙,創(chuàng)建新內(nèi)存空間徐裸。
2、iOS中集合類的深淺拷貝
集合類的拷貝主要分為下面三種類型:
- 淺復(fù)制(shallow copy):在淺復(fù)制操作時(shí)啸盏,對(duì)于被復(fù)制對(duì)象的每一層都是指針復(fù)制重贺。
- 深復(fù)制(one-level-deep copy):在深復(fù)制操作時(shí),對(duì)于被復(fù)制對(duì)象,至少有一層是深復(fù)制气笙。
- 完全復(fù)制(real-deep copy):在完全復(fù)制操作時(shí)次企,對(duì)于被復(fù)制對(duì)象的每一層都是對(duì)象復(fù)制。
來(lái)自蘋(píng)果官方的解釋圖例:
集合的深淺拷貝示例代碼:
- (void)collectionCopy{
NSString *str = [NSString stringWithFormat:@"呵呵噠"];
NSMutableString *mutStr = [NSMutableString stringWithFormat:@"萌萌噠"];
NSArray *arr = [NSArray arrayWithObjects:@"one", @"two", @"three", nil];
// 不可變集合對(duì)象只包含非集合對(duì)象
NSArray *imutArr1 = [NSArray arrayWithObjects:str, mutStr, nil];
// 淺復(fù)制潜圃,創(chuàng)建一個(gè)指針直接指向已有內(nèi)存空間
NSArray *cpyImutArr1 = [imutArr1 copy];
// 深復(fù)制缸棵,創(chuàng)建新的集合內(nèi)存空間,新瓶裝老酒
NSMutableArray *mutCpyImutArr1 = [imutArr1 mutableCopy];
// 創(chuàng)建新的集合內(nèi)存空間谭期,并且對(duì)集合內(nèi)元素進(jìn)行 copy 操作
NSMutableArray *initArr1 = [[NSMutableArray alloc] initWithArray:imutArr1 copyItems:YES];
// 完全深復(fù)制堵第,如果元素是自定義類型,需要實(shí)現(xiàn)NSCoding協(xié)議
NSArray *deepCpyArr1 = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:imutArr1]];
NSLog(@"\n imutArr1 info : pointer = %p; content = %p; first = %p; second = %p; third = %p",&imutArr1, imutArr1, imutArr1[0], imutArr1[1], imutArr1[2]);
// imutArr1 info : pointer = 0x7fff5d946bc8; content = 0x604000443960; first = 0x604000037520; second = 0x604000442d30; third = 0x6040004446e0
NSLog(@"\n cpyImutArr1 info : pointer = %p; content = %p; first = %p; second = %p; third = %p",&cpyImutArr1, cpyImutArr1, cpyImutArr1[0], cpyImutArr1[1], cpyImutArr1[2]);
// cpyImutArr1 info : pointer = 0x7fff5d946bc0; content = 0x604000443960; first = 0x604000037520; second = 0x604000442d30; third = 0x6040004446e0
NSLog(@"\n mutCpyImutArr1 info : pointer = %p; content = %p; first = %p; second = %p; third = %p",&mutCpyImutArr1, mutCpyImutArr1, mutCpyImutArr1[0], mutCpyImutArr1[1], mutCpyImutArr1[2]);
// mutCpyImutArr1 info : pointer = 0x7fff5d946bb8; content = 0x604000444290; first = 0x604000037520; second = 0x604000442d30; third = 0x6040004446e0
NSLog(@"\n initArr1 info : pointer = %p; content = %p; first = %p; second = %p; third = %p",&initArr1, initArr1, initArr1[0], initArr1[1], initArr1[2]);
// initArr1 info : pointer = 0x7fff5d946bb0; content = 0x604000442e80; first = 0x604000037520; second = 0x604000231aa0; third = 0x6040004446e0
NSLog(@"\n deepCpyArr1 info : pointer = %p; content = %p; first = %p; second = %p; third = %p",&deepCpyArr1, deepCpyArr1, deepCpyArr1[0], deepCpyArr1[1], deepCpyArr1[2]);
// deepCpyArr1 info : pointer = 0x7fff5d946ba8; content = 0x604000443210; first = 0x604000231ce0; second = 0x604000444710; third = 0x6040004441d0
// 可變集合對(duì)象包含集合對(duì)象
NSMutableArray *mutArr2 = [NSMutableArray arrayWithObjects:str, mutStr, arr, nil];
// 深復(fù)制隧出,創(chuàng)建新的集合內(nèi)存空間踏志,內(nèi)容不變,新的集合不可變
NSMutableArray *cpyMutArr2 = [mutArr2 copy];
// 深復(fù)制胀瞪,創(chuàng)建新的集合內(nèi)存空間狰贯,內(nèi)容不變
NSMutableArray *mutCpyMutArr2 = [mutArr2 mutableCopy];
NSLog(@"\n mutArr2 info : pointer = %p; content = %p; first = %p; second = %p; third = %p",&mutArr2, mutArr2, mutArr2[0], mutArr2[1], mutArr2[2]);
// mutArr2 info : pointer = 0x7fff53507bc8; content = 0x60400025e2a0; first = 0x60400023fa40; second = 0x60400025d2b0; third = 0x60400025f710
NSLog(@"\n cpyMutArr2 info : pointer = %p; content = %p; first = %p; second = %p; third = %p",&cpyMutArr2, cpyMutArr2, cpyMutArr2[0], cpyMutArr2[1], cpyMutArr2[2]);
// cpyMutArr2 info : pointer = 0x7fff53507bc0; content = 0x60400025d760; first = 0x60400023fa40; second = 0x60400025d2b0; third = 0x60400025f710
NSLog(@"\n mutCpyImutArr1 info : pointer = %p; content = %p; first = %p; second = %p; third = %p",&mutCpyMutArr2, mutCpyMutArr2, mutCpyMutArr2[0], mutCpyMutArr2[1], mutCpyMutArr2[2]);
// mutCpyImutArr1 info : pointer = 0x7fff53507bb8; content = 0x60400025d310; first = 0x60400023fa40; second = 0x60400025d2b0; third = 0x60400025f710
}
通過(guò)代碼和日志可以看出:
不可變集合的拷貝規(guī)律大概是這樣的:
[immutableObject copy] // 淺復(fù)制,指針復(fù)制
[immutableObject mutableCopy] //深復(fù)制赏廓,不是完全復(fù)制涵紊,容器是新的可變?nèi)萜鳎瑑?nèi)容還是舊的可變集合的拷貝規(guī)律大概是這樣的:
[mutableObject copy] //深復(fù)制 返回對(duì)象為immutable
[mutableObject mutableCopy] //深復(fù)制幔摸,不是完全復(fù)制摸柄,容器是新的可變?nèi)萜鳎瑑?nèi)容還是舊的
若想要實(shí)現(xiàn)完全拷貝既忆,則需要使用下面的代碼:
NSArray *newArr = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:oldArr]];
至于拷貝后的集合是否可變驱负,則跟舊集合的是否可變一致。值得注意的是患雇,如果集合內(nèi)含有自定義的類對(duì)象跃脊,則該類需要實(shí)現(xiàn)NSCoding協(xié)議。
如果根據(jù)集合重新init一個(gè)新的集合出來(lái)苛吱,就像這樣子:
NSMutableArray *newArr = [[NSMutableArray alloc] initWithArray:arr copyItems:YES];
那么生成的新集合中的元素酪术,并不一定都是完全拷貝的。因?yàn)閳?zhí)行上面的代碼翠储,只會(huì)對(duì)集合內(nèi)的元素執(zhí)行copy方法绘雁。
3、iOS中自定義對(duì)象的深淺拷貝
iOS中自定義對(duì)象要想實(shí)現(xiàn)拷貝援所,必須要實(shí)現(xiàn)NSCopying或者NSMutableCopying協(xié)議庐舟。
自定義對(duì)象實(shí)現(xiàn)拷貝示例代碼:
// Cat.h
#import <Foundation/Foundation.h>
@interface Cat : NSObject <NSCopying, NSMutableCopying>
@property (nonatomic, assign) NSUInteger age;
@property (nonatomic, copy) NSString *name;
@end
// Cat.m
#import "Cat.h"
@implementation Cat
- (instancetype)copyWithZone:(NSZone *)zone{
return self;
}
- (instancetype)mutableCopyWithZone:(NSZone *)zone{
Cat *cat = [[Cat alloc] init];
cat.name = @"hello";
return cat;
}
@end
從代碼可以看出,自定義類要遵守NSCopy協(xié)議或者NSMutableCopy協(xié)議住拭,拷貝協(xié)議的代碼由自己來(lái)實(shí)現(xiàn)挪略,那么自定義類對(duì)象執(zhí)行Copy或者mutableCopy所得的結(jié)果历帚,無(wú)論是指針層面的淺拷貝,還是完全拷貝杠娱,都是由自己來(lái)決定的挽牢。值得注意的是,如果集合里面包含了自定義類對(duì)象墨辛,那么要用NSKeyedArchiver/NSKeyedUnarchiver實(shí)現(xiàn)集合的完全拷貝,則需要自定義類實(shí)現(xiàn)NSCoding協(xié)議趴俘。
- (id)initWithCoder:(NSCoder *)aDecoder{
self = [super init];
if (self == nil){
return nil;
}
self.name = [aDecoder decodeObjectForKey:@"name"];
// 依次寫(xiě)下想要decode的屬性
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder{
[aCoder encodeObject:self.name forKey:@"name"];
// 依次寫(xiě)下想要encode的屬性
}
(此文完結(jié))
作者:薛小全睹簇,開(kāi)發(fā)者,擅長(zhǎng)Objective-C,HTML,CSS,JavaScript寥闪。