美文网首页iOS高级实用
✎iOS中的深拷贝和浅拷贝,你真的理解了吗?

✎iOS中的深拷贝和浅拷贝,你真的理解了吗?

作者: 树懒啊树懒 | 来源:发表于2017-02-17 11:43 被阅读230次

    首先分类一下:(1) 非容器 , 容器 : (2) 不可变对象,可变对象

    \1. 非容器不可变对象,比如NSString

    2.非容器可变对象:比如NSMutableString

    3.容器类不可变对象: 比如NSArray

    4.容器类可变对象: 比如NSMutableArray

    在观察深浅拷贝之前先得了解一下retain,copy和mutableCopy的特点,特点如下:

    1.retain:始终是浅复制。引用计数每次加一。返回对象是否可变与被复制的对象保持一致。(所以后面不再举例,需要测试的在手动管理上)

    2.copy:对于可变对象为深复制,引用计数不改变;对于不可变对象是浅复制, 引用计数每次加一。始终返回一个不可变对象。

    3.mutableCopy:始终是深复制,引用计数不改变。始终返回一个可变对象。

    下面把容器非容器,可变非可变结合着retain,copy,mutableCopy来仔细的分析一下OC中得深浅拷贝,代码走起:

    1.非容器 + 不可变对象 + retain + copy + mutableCopy

    NSString *str = @"InteviewTestCode";
    
    //把str通过copy的方式把值赋给str2
    
    NSString *str2 = [str copy];
    
    //把str通过mutableCopy的方式把值赋给str3
    
    NSString *str3 = [str mutableCopy];
    
    
    //分别输出每个字符串的内存地址
    
    NSLog(@" str-p = %p", str);
    
    NSLog(@"str2-p = %p", str2);
    
    NSLog(@"str3-p = %p", str3);
    

    代码运行结果分析:

    2017-02-17 09:57:02.690 InteviewTestCode[18890:507384]  str-p = 0x10d185110
    
    2017-02-17 09:57:02.690 InteviewTestCode[18890:507384] str2-p = 0x10d185110
    
    2017-02-17 09:57:02.691 InteviewTestCode[18890:507384] str3-p = 0x60800006fd80
    

    \1. 对于非容器类的不可变对象retain和copy为浅拷贝,mutableCopy为深拷贝

    浅拷贝获得的对象的地址和原有对象的地址一致

    3.而深拷贝返回新的内存地址,并且返回的对象为可变对象

    2.非容器 + 可变对象 + retain + copy + mutableCopy

    接下来我们来测试下非容器类的可变对象的深浅拷贝

    代码如下:

    Objective-C

    //2.非容器 + 可变对象 + retain + copy + mutableCopy
    
    NSMutableString *s = [NSMutableString stringWithFormat:@"InteviewTestCode"];
    
    //把s通过copy的方式把值赋给s2;
    
    NSMutableString *s2 = [s copy];
    
    //把s通过mutableCopy的方式把值赋给s3
    
    NSMutableString *s3 = [s mutableCopy];
    
    ​
    
    //打印每个非容器类可变对象的地址
    
    NSLog(@" s_p = %p", s);
    
    NSLog(@"s2_p = %p", s2);
    
    NSLog(@"s3_p = %p", s3);
    
    ​```
    
    运行结果分析:
    

    InteviewTestCode[18890:507384] s_p = 0x600000071580

    2017-02-17 09:57:02.692 InteviewTestCode[18890:507384] s2_p = 0xa6968736164756c7

    2017-02-17 09:57:02.692 InteviewTestCode[18890:507384] s3_p = 0x600000071280

    1.retian对对可变对象为浅拷贝
    
    2.copy对可变对象非容器类为深拷贝
    
    3.mutableCopy对可变非容器类为深拷贝
    
    **3.容器类 +  非可变对象 + retain + copy + mutableCopy**
    
    容器类的非可变对象进行测试:容器需要特别注意的是容器本身和容器内的对象分别是什么结果,(1)从容器对象看而言是容器的深拷贝, (2)但从输出容器中的元素是容器的浅拷贝(所以下面代码并不是容器真正意义上的的完全拷贝,本文最后做介绍)。
    

    NSMutableString *string = [NSMutableString stringWithFormat:@"ludashi"];

    //第二种:容器类不可变对象拷贝

    NSArray *array = [NSArray arrayWithObjects:string, @"b", nil];

    //把array通过copy的方式把值赋给array2

    NSArray *array2 = [array copy];

    //把array通过mutableCopy方式把值赋给array3

    NSArray *array3 = [array mutableCopy];

    //分别输出每个地址

    NSLog(@"分别输出每个地址");

    NSLog(@" array_p = %p", array);

    NSLog(@"array2_p = %p", array2);

    NSLog(@"array3_p = %p", array3);

    //分别输出每个地址

    NSLog(@"分别输出拷贝后数组中第一个元素的地址");

    NSLog(@" array_p[0] = %p", array[0]);

    NSLog(@"array2_p[0] = %p", array2[0]);

    NSLog(@"array3_p[0] = %p", array3[0]);

    运行结果:
    

    2017-02-17 10:29:18.065 InteviewTestCode[18973:524282] 分别输出每个地址

    2017-02-17 10:29:18.065 InteviewTestCode[18973:524282] array_p = 0x608000036600

    2017-02-17 10:29:18.065 InteviewTestCode[18973:524282] array2_p = 0x608000036600

    2017-02-17 10:29:18.066 InteviewTestCode[18973:524282] array3_p = 0x6080000498d0

    2017-02-17 10:29:18.066 InteviewTestCode[18973:524282] 分别输出拷贝后数组中第一个元素的地址

    2017-02-17 10:29:18.066 InteviewTestCode[18973:524282] array_p[0] = 0x608000074340

    2017-02-17 10:29:18.066 InteviewTestCode[18973:524282] array2_p[0] = 0x608000074340

    2017-02-17 10:29:18.078 InteviewTestCode[18973:524282] array3_p[0] = 0x608000074340

    容器类的非可变对象:
    
    copy拷贝获得的对象的地址和原有对象的地址一致
    
    2.而mutableCopy拷贝返回新的内存地址,并且返回的对象为可变对象
    
    3.而容器内对象的地址总是一样的,不受copy,mutableCopy影响
    
    **4.容器类 +  可变对象 + retain + copy + mutableCopy**
    
    下面对容器类的可变对象进行测试,copy和mutableCopy对于容器本身是深拷贝,原因是返回了一个新的容器地址,但对于容器中的元素仍然是浅拷贝。
    
    代码如下:
    

    //第四种:容器类的可变对象的拷贝,用NSMutableArray来实现

    NSMutableArray *m_array = [NSMutableArray arrayWithObjects:string, nil];

    NSMutableArray *m_array2 = [m_array copy];

    //把m_array通过mytableCopy把值赋给m_array3

    NSMutableArray *m_array3 = [m_array mutableCopy];

    //打印输出每个可变容器对象的地址

    NSLog(@"打印输出每个可变容器对象的地址");

    NSLog(@" m_array_p = %p", m_array);

    NSLog(@"m_array_p2 = %p", m_array2);

    NSLog(@"m_array_p3 = %p", m_array3);

    //打印输出每个可变容器中元素的地址

    NSLog(@"打印输出每个可变容器中元素的地址");

    NSLog(@" m_array_p[0] = %p", m_array[0]);

    NSLog(@"m_array_p2[0] = %p", m_array2[0]);

    NSLog(@"m_array_p3[0] = %p", m_array3[0]);

    运行结果:
    

    2017-02-17 10:29:18.078 InteviewTestCode[18973:524282] 打印输出每个可变容器对象的地址

    2017-02-17 10:29:18.078 InteviewTestCode[18973:524282] m_array_p = 0x608000049780

    2017-02-17 10:29:18.079 InteviewTestCode[18973:524282] m_array_p2 = 0x60800001a0a0

    2017-02-17 10:29:18.079 InteviewTestCode[18973:524282] m_array_p3 = 0x608000049300

    2017-02-17 10:29:18.079 InteviewTestCode[18973:524282] 打印输出每个可变容器中元素的地址

    2017-02-17 10:29:18.079 InteviewTestCode[18973:524282] m_array_p[0] = 0x608000074340

    2017-02-17 10:29:18.080 InteviewTestCode[18973:524282] m_array_p2[0] = 0x608000074340

    2017-02-17 10:29:18.080 InteviewTestCode[18973:524282] m_array_p3[0] = 0x608000074340

    容器类的可变对象:
    
    1.copy:对于可变对象为深复制,引用计数不改变;对于不可变对象是浅复制, 引用计数每次加一。始终返回一个不可变对象。
    
    2.mutableCopy:始终是深复制,引用计数不改变。始终返回一个可变对象。
    
    **5.自定义类对象之间的深浅拷贝问题**
    
    在Objective-C中并不是所有的类都支持拷贝;只有遵循NSCopying协议的类,才支持copy拷贝,只有遵循NSMutableCopying协议的类,才支持mutableCopy拷贝。如果没有遵循拷贝协议,拷贝时会出错。
    
    如果我们想再我们自定义的类中支持copy和mutableCopy那么我们就需要使我们定义的类遵循NSCopying和NSMutableCopying协议,代码如下:
    

    @interface CopyTestObj : NSObject<NSCopying,NSMutableCopying>

    @end

    然后再重写-(id) copyWithZone : (NSZone *) zone  和 -(id)mutableCopyWithZone : (NSZone *) zone
    
    重写-(id) copyWithZone :(NSZone *)zone方法如下
    

    //浅拷贝

    -(id) copyWithZone:(NSZone *) zone {

    CopyTestObj *test = [[CopyTestObj allocWithZone : zone] init];

    return test;

    }

    //深拷贝

    -(id) mutableCopyWithZone : (NSZone *) zone {

    CopyTestObj *test = [[CopyTestObj allocWithZone : zone] init];

    //区别:对属性也进行复制

    test.typeStr = self.typeStr;

    return test;

    }

    6.我们如何实现容器中的完全拷贝呢?上面没有考虑到容器中元素拷贝的问题,下面进行代码补充
    
    首先:遵循NSCoder协议
    

    @interface CopyTestObj : NSCoder<NSCopying,NSMutableCopying>

    代码如下:
    

    //新建一个测试字符串

    NSMutableString * str = [NSMutableString stringWithFormat:@"ludashi__"];

    //新建一个测试字典

    NSMutableDictionary *dic = [NSMutableDictionary dictionaryWithCapacity:1];

    [dic setObject:str forKey:@"key1"];

    //把字典存入数组中

    NSMutableArray *oldArray = [NSMutableArray arrayWithObject:dic];

    //用就得数组生成新的数组

    NSMutableArray *newArray = [NSMutableArray arrayWithArray:oldArray];

    //用copyItems拷贝数组中的元素

    NSMutableArray *copyItems = [[NSMutableArray alloc] initWithArray:oldArray copyItems:YES];

    //把数组归档成一个NSData,然后再实现完全拷贝

    NSData * data = [NSKeyedArchiver archivedDataWithRootObject:oldArray];

    NSMutableArray *multable = [NSKeyedUnarchiver unarchiveObjectWithData:data];

    //往字典中加入新的值

    [dic setObject:@"new_value1" forKey:@"key2"];

    //改变str的值

    [str appendString:@"update"];

    NSLog(@"%@", oldArray);

    NSLog(@"%@", newArray);

    NSLog(@"%@", copyItems);

    NSLog(@"%@", multable);

    //每个数组的地址为:

    NSLog(@"%p", oldArray);

    NSLog(@"%p", newArray);

    NSLog(@"%p", copyItems);

    NSLog(@"%p", multable);

    运行结果:
    

    2014-08-13 16:33:00.752 OC6-1[3942:303] (

    {

    key1 = "ludashi__update";

    key2 = "new_value1";

    }

    )

    2014-08-13 16:33:00.753 OC6-1[3942:303] (

    {

    key1 = "ludashi__update";

    key2 = "new_value1";

    }

    )

    2014-08-13 16:33:00.753 OC6-1[3942:303] (

    {

    key1 = "ludashi__update";

    }

    )

    2014-08-13 16:33:00.753 OC6-1[3942:303] (

    {

    key1 = "ludashi__";

    }

    )

    2014-08-13 16:33:00.754 OC6-1[3942:303] 0x100204560

    2014-08-13 16:33:00.754 OC6-1[3942:303] 0x1002046d0

    2014-08-13 16:33:00.754 OC6-1[3942:303] 0x1002047c0

    2014-08-13 16:33:00.755 OC6-1[3942:303] 0x100406610

    可以看出,通过数组归档可以实现容器深复制 和 容器里的 对象 也同时深复制(地址不同)

    相关文章

      网友评论

        本文标题:✎iOS中的深拷贝和浅拷贝,你真的理解了吗?

        本文链接:https://www.haomeiwen.com/subject/vggkwttx.html