iOS常见问题(1): 浅拷贝与深拷贝

本人所有文章目录:http://my.oschina.net/ChenTF/blog/677112
本篇文章地址: http://my.oschina.net/ChenTF/blog/1789532
转载请注明出处

在开始之前, 先来个测验:

  1. 对NSString执行copy后, 是深拷贝还是浅拷贝?
  2. 对NSMutableString执行copy后, 是深拷贝还是浅拷贝?
  3. 对数组进行Copy后, 是深拷贝还是浅拷贝?
  4. 对数组进行Copy后, 修改旧数组内指针对象所指向的内容, 新数组对象内容是否改变?
  5. 对数组进行mutableCopy呢?
  6. NSArray *newArray = [[NSArray alloc] initWithArray:oldArray]. 对newArray的元素所指的指针进行操作, 会影响oldArray吗?
  7. 如何实现数组的内容Copy?(一维数组)

答案:

  1. 浅拷贝
  2. 深拷贝
  3. 浅拷贝
  4. 改变
  5. 深拷贝, 改变
  6. 会影响
  7. initWithArray:copyItems:

争议点: 数组的内容是指针列表, 对数组进行mutableCopy时, 数组的内容已经开辟了新的空间进行了复制. 我觉得对数组对象而言已经是深拷贝. 当然有些同学觉得没有对数组所指对象进行重新分配空间, 不属于深拷贝.

如果你对以上问题都答对了, 则请忽略本文.

以下是正文:


概念:

  • 浅拷贝 / 指针拷贝: 再有指针的情况下, 增加一个指针指向已经存在的内存 (OC中引用计数会+1)
  • 深拷贝 / 对象拷贝: 增加一个指针, 并且申请一个新的内存, 使新指针指向新内存.

1.对一个不可变对象执行Copy, MutableCopy时, 会发生什么?

     TFLog宏:
     #define TFLog(x) (NSLog(@"x = %p : %@, retainCount = %lu", x, x, x.retainCount))

     NSString *str = @"origion";
     NSString *stringCopy = [str copy];
     NSMutableString *stringMCopy = [str mutableCopy];
     [stringMCopy appendString:@"!!"];
     
     TFLog(str);
     TFLog(stringCopy);
     TFLog(stringMCopy);

     /* Log:
     2018-04-02 11:47:00.496571+0800 StudyCopy[9955:783264] x = 0x10c795060 : origion, retainCount = 18446744073709551615
     2018-04-02 11:47:00.496703+0800 StudyCopy[9955:783264] x = 0x10c795060 : origion, retainCount = 18446744073709551615
     2018-04-02 11:47:00.496785+0800 StudyCopy[9955:783264] x = 0x600000256560 : origion!!, retainCount = 1
     */

从Log可以看出str与stringCopy的内存地址一致, stringMCopy与str的地址不一样

结论:

  • copy是浅拷贝;
  • mutableCopy是深拷贝

2.对一个可变对象执行Copy, MutableCopy时, 会发生什么?

     NSMutableString *string = [NSMutableString stringWithString: @"origion"];
     NSString *stringCopy = [string copy];
     NSMutableString *mStringCopy = [string copy];
     NSMutableString *stringMCopy = [string mutableCopy];
     
     TFLog(string);
     TFLog(stringCopy);
     TFLog(mStringCopy);
     TFLog(stringMCopy);
     /* Log:
     2018-04-02 15:51:16.104555+0800 StudyCopy[10470:931102] x = 0x60400044f870 : origion, retainCount = 1
     2018-04-02 15:51:16.104712+0800 StudyCopy[10470:931102] x = 0xa6e6f696769726f7 : origion, retainCount = 18446744073709551615
     2018-04-02 15:51:16.104806+0800 StudyCopy[10470:931102] x = 0xa6e6f696769726f7 : origion, retainCount = 18446744073709551615
     2018-04-02 15:51:16.104889+0800 StudyCopy[10470:931102] x = 0x60400044ea30 : origion, retainCount = 1
     */

    //  [mStringCopy appendString:@"mm"];  //error: copy的结果是不可变对象
     [string appendString:@" origion!"];
     [stringMCopy appendString:@"!!"];
     
     TFLog(string);
     TFLog(stringCopy);
     TFLog(mStringCopy);
     TFLog(stringMCopy);
     /* Log:
     2018-04-02 15:53:54.796318+0800 StudyCopy[10490:933693] x = 0x60000025ab80 : origion origion!, retainCount = 1
     2018-04-02 15:53:54.796453+0800 StudyCopy[10490:933693] x = 0xa6e6f696769726f7 : origion, retainCount = 18446744073709551615
     2018-04-02 15:53:54.796531+0800 StudyCopy[10490:933693] x = 0xa6e6f696769726f7 : origion, retainCount = 18446744073709551615
     2018-04-02 15:53:54.796605+0800 StudyCopy[10490:933693] x = 0x6000002593e0 : origion!!, retainCount = 1
     */

看Log1: mStringCopy , mStringCopy, stringMCopy, 三个的内存地址都不一样, 所以得出结论2.1
看Log2: 执行完appendString后, string与stringMCopy都重新分配了地址, 可得出结论2.2

结论:
2.1 Mutable 的 Copy, mutableCopy 都是深拷贝
2.2 [NSMutableString appendString] 方法会抛弃之前的内存地址, 开辟新的内存地址, 然后赋值给当前对象;
2.3 copy返回的是不可变对象, mutableCopy是可变对象, 与深浅拷贝是两件事情.
3.4 无论是深拷贝, 还是浅拷贝, 都是为了保证执行Copy后, 修改任一一方, 对另一方不影响, 所以iMutable只需要实现浅拷贝即可, 而Mutable需要实现深拷贝才可以.

3.对不可变/可变 数组进行Copy, mutableCopy, 是深拷贝还是浅拷贝?

    NSArray *array1 = [[NSArray alloc] initWithArray:@[@"a", @"b", @"c", @"d"]];
    NSArray *arrayCopy1 = [array1 copy];
    NSMutableArray *mArrayCopy1 = [array1 mutableCopy];
    
    NSLog(@" ============ 1 =============");
    TFLog(array1);
    TFLog(arrayCopy1);
    TFLog(mArrayCopy1);
    /* Log:
     2018-04-03 15:31:06.168695+0800 StudyCopy[15196:1840616] x = 0x60000045a2e0 : (
     a, 0x1097f20a0, retainCount = 18446744073709551615
     b, 0x1097f20c0, retainCount = 18446744073709551615
     c, 0x1097f20e0, retainCount = 18446744073709551615
     d, 0x1097f2100, retainCount = 18446744073709551615
     )
     , retainCount = 2
     2018-04-03 15:31:06.168841+0800 StudyCopy[15196:1840616] x = 0x60000045a2e0 : (
     a, 0x1097f20a0, retainCount = 18446744073709551615
     b, 0x1097f20c0, retainCount = 18446744073709551615
     c, 0x1097f20e0, retainCount = 18446744073709551615
     d, 0x1097f2100, retainCount = 18446744073709551615
     )
     , retainCount = 2
     2018-04-03 15:31:06.168988+0800 StudyCopy[15196:1840616] x = 0x60000045cfb0 : (
     a, 0x1097f20a0, retainCount = 18446744073709551615
     b, 0x1097f20c0, retainCount = 18446744073709551615
     c, 0x1097f20e0, retainCount = 18446744073709551615
     d, 0x1097f2100, retainCount = 18446744073709551615
     )
     , retainCount = 1
     
     结论:
     3.1 Array 的copy是浅拷贝, mutableCopy是深拷贝, 但内容不变
     */

     NSLog(@" ============ 2 =============");
    [mArrayCopy1 addObject:@"de"];
    TFLog(mArrayCopy1);
    /*Log:
     2018-04-03 14:31:51.231321+0800 StudyCopy[14904:1800741]  ============ 2 =============
     2018-04-03 15:31:06.169435+0800 StudyCopy[15196:1840616] x = 0x60000045cfb0 : (
     a, 0x1097f20a0, retainCount = 18446744073709551615
     b, 0x1097f20c0, retainCount = 18446744073709551615
     c, 0x1097f20e0, retainCount = 18446744073709551615
     d, 0x1097f2100, retainCount = 18446744073709551615
     de, 0x1097f2180, retainCount = 18446744073709551615
     )
     , retainCount = 1
     )*/ 

    NSLog(@" ============ 3 =============");
    [mArrayCopy1 removeObjectAtIndex:0];
    NSArray *array2 = [mArrayCopy1 copy];
    NSMutableArray *array3 = [mArrayCopy1 mutableCopy];
    
    TFLog(mArrayCopy1);
    TFLog(array2);
    TFLog(array3);
    /*Log:
     2018-04-03 15:38:47.577031+0800 StudyCopy[15234:1846891] x = 0x60400024aa10 : (
     b, 0x101ebd0c0, retainCount = 18446744073709551615
     c, 0x101ebd0e0, retainCount = 18446744073709551615
     d, 0x101ebd100, retainCount = 18446744073709551615
     de, 0x101ebd180, retainCount = 18446744073709551615
     )
     , retainCount = 1
     2018-04-03 15:38:47.612703+0800 StudyCopy[15234:1846891] x = 0x60400024a860 : (
     b, 0x101ebd0c0, retainCount = 18446744073709551615
     c, 0x101ebd0e0, retainCount = 18446744073709551615
     d, 0x101ebd100, retainCount = 18446744073709551615
     de, 0x101ebd180, retainCount = 18446744073709551615
     )
     , retainCount = 1
     2018-04-03 15:38:47.612841+0800 StudyCopy[15234:1846891] x = 0x60400024ab60 : (
     b, 0x101ebd0c0, retainCount = 18446744073709551615
     c, 0x101ebd0e0, retainCount = 18446744073709551615
     d, 0x101ebd100, retainCount = 18446744073709551615
     de, 0x101ebd180, retainCount = 18446744073709551615
     )
     , retainCount = 1
     
     结论:
     3.2 对MArray进行add, 或 remove, 并不会改变数组的指针, 只会更改数组内的元素
     3.3 [MutableArray copy] 是深拷贝 [MutableArray mutableCopy] 也是深拷贝, 内容不变
     */

通过分析Log1中 array1, arrayCopy1的地址一致, 可得出 不可变数组 copy是浅拷贝;
通过分析Log1中 array1, mArrayCopy1的地址不一致, 可得出 不可变数组 mutableCopy是深拷贝;
通过分析Log3中 mArrayCopy1, array2, array3的地址不一致, 可得出: 可变数组, copy, mutableCopy都是深拷贝

结论:
3.1 Array 的copy是浅拷贝, mutableCopy是深拷贝, 但内容不变
3.2 对MArray进行add, 或 remove, 并不会改变数组的指针, 只会更改数组内的元素
3.3 [MutableArray copy] 是深拷贝 [MutableArray mutableCopy] 也是深拷贝, 内容不变
3.4 无论数组是深拷贝, 还是浅拷贝, 也只是拷贝数组的指针与对象, 数组内元素都是浅拷贝

4.如何实现数组内容的拷贝?

为了保证非系统对NSString进行了优化, 所以自定义了Person类:

@interface Person : NSObject<NSCopying, NSMutableCopying>
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSUInteger *age;
- (instancetype)initWithName:(NSString *)name Age:(NSUInteger)age;
@end

@implementation Person
- (instancetype)initWithName:(NSString *)name Age:(NSUInteger)age {
    self = [super init];
    if (self) {
        _name = [name copy];
        _age = age;
    }
    return self;
}
- (id)copyWithZone:(NSZone *)zone {
    NSLog(@"person copyWithZone 被调用");
    Person *p = [[Person allocWithZone:zone] init];//[self copyWithZone:zone];
    p.name = [self.name copy];
    p.age = self.age;
    return p;
}
- (id)mutableCopyWithZone:(nullable NSZone *)zone {
    NSLog(@"person mutableCopyWithZone 被调用");
    Person *p = [[Person allocWithZone:zone] init];//[self copyWithZone:zone];
    p.name = [self.name copy];
    p.age = self.age;
    return p;
}
@end

Example:

    Person *p1 = [[Person alloc] initWithName:@"小明" Age:11];
    Person *p2 = [[Person alloc] initWithName:@"小明" Age:22];
    Person *p3 = [[Person alloc] initWithName:@"小红" Age:11];
    Person *p4 = [[Person alloc] initWithName:@"小红" Age:22];
    
    NSArray *array1 = [NSArray arrayWithObjects:p1, p2, p3, p4, nil];
    NSArray *array2 = [array1 copy];
    
    NSMutableArray *mArray1 = [array1 mutableCopy];
    NSMutableArray *mArray2 = [mArray1 mutableCopy];
    NSArray *array3 = [mArray1 copy];
    
    NSLog(@" ============ 1 =============");
    TFLog(array1);
    TFLog(array2);
    TFLog(mArray1);
    
    TFLog(mArray2);
    TFLog(array3);
    
    /*
     2018-04-03 16:16:02.203387+0800 StudyCopy[15501:1883285]  ============ 1 =============
     2018-04-03 16:16:02.204392+0800 StudyCopy[15501:1883285] x = 0x60000025ed20 : (
     <Person: 0x600000421040>, 0x600000421040, retainCount = 3
     <Person: 0x600000421020>, 0x600000421020, retainCount = 3
     <Person: 0x600000420fe0>, 0x600000420fe0, retainCount = 3
     <Person: 0x6000004210a0>, 0x6000004210a0, retainCount = 3
     )
     , retainCount = 2
     2018-04-03 16:16:02.204560+0800 StudyCopy[15501:1883285] x = 0x60000025ed20 : (
     <Person: 0x600000421040>, 0x600000421040, retainCount = 3
     <Person: 0x600000421020>, 0x600000421020, retainCount = 3
     <Person: 0x600000420fe0>, 0x600000420fe0, retainCount = 3
     <Person: 0x6000004210a0>, 0x6000004210a0, retainCount = 3
     )
     , retainCount = 2
     2018-04-03 16:16:02.204733+0800 StudyCopy[15501:1883285] x = 0x60000025f200 : (
     <Person: 0x600000421040>, 0x600000421040, retainCount = 3
     <Person: 0x600000421020>, 0x600000421020, retainCount = 3
     <Person: 0x600000420fe0>, 0x600000420fe0, retainCount = 3
     <Person: 0x6000004210a0>, 0x6000004210a0, retainCount = 3
     )
     , retainCount = 1
     2018-04-03 16:16:02.204862+0800 StudyCopy[15501:1883285] x = 0x60000025f140 : (
     <Person: 0x600000421040>, 0x600000421040, retainCount = 3
     <Person: 0x600000421020>, 0x600000421020, retainCount = 3
     <Person: 0x600000420fe0>, 0x600000420fe0, retainCount = 3
     <Person: 0x6000004210a0>, 0x6000004210a0, retainCount = 3
     )
     , retainCount = 1
     2018-04-03 16:16:02.205016+0800 StudyCopy[15501:1883285] x = 0x60000025eff0 : (
     <Person: 0x600000421040>, 0x600000421040, retainCount = 3
     <Person: 0x600000421020>, 0x600000421020, retainCount = 3
     <Person: 0x600000420fe0>, 0x600000420fe0, retainCount = 3
     <Person: 0x6000004210a0>, 0x6000004210a0, retainCount = 3
     )
     , retainCount = 1
     */

结论:

4.1 对可变, 不可变数组执行 copy, mutableCopy的结果都不会影响数组内的元素, 数组内元素仍是浅拷贝.
无论执行的是 initWithArray 方法还是Copy, 结果都一致

Example:

    NSLog(@" ============ 2 =============");
    NSArray *array11 = [[NSArray alloc] initWithArray:array1];
    NSArray *array12 = [[NSArray alloc] initWithArray:array1 copyItems:YES];
    NSMutableArray *mArray11 = [[NSMutableArray alloc] initWithArray:array1];
    NSMutableArray *mArray12 = [[NSMutableArray alloc] initWithArray:array1 copyItems:YES];
    TFLog(array11);
    TFLog(array12);
    TFLog(mArray11);
    TFLog(mArray12);
    
    /* Log:
     2018-04-03 16:16:02.205103+0800 StudyCopy[15501:1883285]  ============ 2 =============
     
     2018-04-03 16:28:50.427949+0800 StudyCopy[15578:1895787] person copyWithZone 被调用
     2018-04-03 16:28:50.428040+0800 StudyCopy[15578:1895787] person copyWithZone 被调用
     2018-04-03 16:28:50.428109+0800 StudyCopy[15578:1895787] person copyWithZone 被调用
     2018-04-03 16:28:50.428197+0800 StudyCopy[15578:1895787] person copyWithZone 被调用
     2018-04-03 16:28:50.428434+0800 StudyCopy[15578:1895787] person copyWithZone 被调用
     2018-04-03 16:28:50.428556+0800 StudyCopy[15578:1895787] person copyWithZone 被调用
     2018-04-03 16:28:50.429276+0800 StudyCopy[15578:1895787] person copyWithZone 被调用
     2018-04-03 16:28:50.429357+0800 StudyCopy[15578:1895787] person copyWithZone 被调用
     
     2018-04-03 16:16:05.699864+0800 StudyCopy[15501:1883285] x = 0x60400024e6a0 : (
     <Person: 0x600000421040>, 0x600000421040, retainCount = 5
     <Person: 0x600000421020>, 0x600000421020, retainCount = 5
     <Person: 0x600000420fe0>, 0x600000420fe0, retainCount = 5
     <Person: 0x6000004210a0>, 0x6000004210a0, retainCount = 5
     )
     , retainCount = 1
     2018-04-03 16:16:05.700044+0800 StudyCopy[15501:1883285] x = 0x60400024e940 : (
     <Person: 0x604000236600>, 0x604000236600, retainCount = 1
     <Person: 0x604000236400>, 0x604000236400, retainCount = 1
     <Person: 0x604000236760>, 0x604000236760, retainCount = 1
     <Person: 0x604000236640>, 0x604000236640, retainCount = 1
     )
     , retainCount = 1
     2018-04-03 16:16:05.700265+0800 StudyCopy[15501:1883285] x = 0x60400024e700 : (
     <Person: 0x600000421040>, 0x600000421040, retainCount = 5
     <Person: 0x600000421020>, 0x600000421020, retainCount = 5
     <Person: 0x600000420fe0>, 0x600000420fe0, retainCount = 5
     <Person: 0x6000004210a0>, 0x6000004210a0, retainCount = 5
     )
     , retainCount = 1
     2018-04-03 16:16:05.700456+0800 StudyCopy[15501:1883285] x = 0x60400024e9a0 : (
     <Person: 0x604000236680>, 0x604000236680, retainCount = 1
     <Person: 0x6040002366a0>, 0x6040002366a0, retainCount = 1
     <Person: 0x6040002365e0>, 0x6040002365e0, retainCount = 1
     <Person: 0x604000236700>, 0x604000236700, retainCount = 1
     )
     , retainCount = 1
     结论:
     4.2 NSArray想实现数组内容也拷贝, 只有通过 initWithArray:copyItems: 方法才可以 ,
         同时 会间接调用copyWithZon:的方法, mutableCopyWithZone:不会调用
     */

结论:
通过 array1 , array12, marray22的 元素内容的地址都不相同, 可知 initWithArray:copyItems: 会调用NSCoping协议的copyWithZone方法, 达到内容拷贝的效果.

回顾总结:

对象:

不可变 copy -> 浅拷贝; mutableCopy -> 深拷贝
可变 copy -> 深拷贝; mutableCopy -> 深拷贝

数组:

不可变 copy -> 浅拷贝; mutableCopy -> 深拷贝
可变 copy -> 深拷贝; mutableCopy -> 深拷贝

对数组进行copy, 内容都不会进行深拷贝


觉得对你有帮助, 想请我喝杯果汁?
微信收钱码 支付宝收钱码

猜你喜欢

转载自my.oschina.net/ChenTF/blog/1789532