zoukankan      html  css  js  c++  java
  • 深浅拷贝

    说法一

    深浅拷贝分别指深拷贝和浅拷贝,即mutableCopy和copy方法。
    copy复制一个不可变对象,而mutableCopy复制一个mutable可变对象。

    什么时候用到深浅拷贝?下面举几个例子说明。

    非容器类对象

    如NSString,NSNumber等一类对象
    示例1:

    1
    2
    3
    4
    5
        // 非容器类对象
        NSString *str = @"origin string";
        NSString *strCopy = [str copy];
        NSMutableString *mstrCopy = [str mutableCopy];
        [mstrCopy appendString:@"??"];

    查看内存可以发现,str和strCopy指向的是同一块内存区域,我们称之为弱引用(weak reference)。而mstrCopy是真正的复制,系统为其分配了新内存空间,保存从str复制过来的字符串值。从最后一行代码中修改这些值而不影 响str和strCopy中可证明。

    示例2:

    1
    2
    3
    4
    5
    6
    7
        NSMutableString *mstr = [NSMutableString stringWithString:@"origin"];
        NSString *strCopy = [mstr copy];
        NSMutableString *mstrCopy = [mstr copy];
        NSMutableString *mstrMCopy = [mstr mutableCopy];
        //[mstrCopy appendString:@"1111"];  //error
        [mstr appendString:@"222"];
        [mstrMCopy appendString:@"333"];

    以上四个对象所分配的内存都是不一样的。而且对于mstrCopy,它所指向的其实是一个imutable对象,是不可改变的,所以会出错。这点要注意,好好理解。

    小结:
    对于非容器类对象,有:

    • 如果对一个不可变对象复制,copy是指针复制,即浅拷贝;而mutableCopy则是对象复制,即深拷贝。(示例1)
    • 如果是对可变对象复制,都是深拷贝,但copy复制返回的对象是不可变的。(示例2)

    容器类对象深浅复制

    比如NSArray,NSDictionary等。对于容器类本身,上面讨论的结论也适用的,下面探讨的是复制后容器内对象的变化。

    示例3

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    /* copy返回不可变对象,mutablecopy返回可变对象 */
        
        NSArray *array1     = [NSArray arrayWithObjects:@"a",@"b",@"c",nil];
        NSArray *arrayCopy1 = [array1 copy];
        //arrayCopy1是和array同一个NSArray对象(指向相同的对象),包括array里面的元素也是指向相同的指针
        NSLog(@"array1 retain count: %d",[array1 retainCount]);
        NSLog(@"array1 retain count: %d",[arrayCopy1 retainCount]);
        
        NSMutableArray *mArrayCopy1 = [array1 mutableCopy];
        //mArrayCopy1是array1的可变副本,指向的对象和array1不同,但是其中的元素和array1中的元素指向的还是同一个对象。mArrayCopy1还可以修改自己的对象
        [mArrayCopy1 addObject:@"de"];
        [mArrayCopy1 removeObjectAtIndex:0];

    array1和arrayCopy1是指针复制,而mArrayCopy1是对象复制,符合前面示例1讨论的结论。mArrayCopy1可以改变其内的元素:删除或添加。但容器内的元素内容都是浅拷贝。

    示例4

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
        NSArray *mArray1 = [NSArray arrayWithObjects:[NSMutableStringstringWithString:@"a"],@"b",@"c",nil];
        NSLog(@"mArray1 retain count: %d",[mArray1 retainCount]);
        NSArray *mArrayCopy2 = [mArray1 copy];
        NSLog(@"mArray1 retain count: %d",[mArray1 retainCount]);
        // mArray1和mArrayCopy2指向同一对象,retain值+1。
        
        NSMutableArray *mArrayMCopy1 = [mArray1 mutableCopy];
        NSLog(@"mArray1 retain count: %d",[mArray1 retainCount]);
        //mArrayCopy2和mArray1指向的是不一样的对象,但是其中的元素都是一样的对象——同一个指针

        NSMutableString *testString = [mArray1 objectAtIndex:0];
        //testString = @"1a1";//这样会改变testString的指针,其实是将@“1a1”临时对象赋给了testString
        [testString appendString:@" tail"];//这样以上三个数组的首元素都被改变了

    由此可见,对于容器而言,其元素对象始终是指针复制。如果需要元素对象也是对象复制,就需要实现深拷贝。http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Collections/Articles/Copying.html

    示例5

    1
    2
    3
    4
        NSArray *array = [NSArray arrayWithObjects:[NSMutableString stringWithString:@"first"],[NSStringstringWithString:@"b"],@"c",nil];
        NSArray *deepCopyArray=[[NSArray alloc] initWithArray: array copyItems: YES];
        NSArray* trueDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData:
        [NSKeyedArchiver archivedDataWithRootObject: array]];

    trueDeepCopyArray是完全意义上的深拷贝,而deepCopyArray则不是,对于deepCopyArray内的不可变元素其还是指针复制。
    或者我们自己实现深拷贝的方法。因为如果容器的某一元素是不可变的,那你复制完后该对象仍旧是不能改变的,因此只需要指针复制即可。除非你对容器内的元素重新赋值,否则指针复制即已足够。
    举个例子,[[array objectAtIndex:0]appendstring:@”sd”]后其他的容器内对象并不会受影响。[[array objectAtIndex:1]和[[deepCopyArray objectAtIndex:0]尽管是指向同一块内存,但是我们没有办法对其进行修改——因为它是不可改变的。所以指针复制已经足够。所以这并不是完全 意义上的深拷贝。

    自己实现深拷贝的方法

    NSDictionaryMutableDeepCopy.h

    1
    2
    3
    4
    5
    6
    7
    8
    #import <foundation /Foundation.h>

    @interface NSDictionary(MutableDeepCopy)

    - (NSMutableDictionary *)mutableDeepCopy;

    @end
    </foundation>

    NSDictionaryMutableDeepCopy.m

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    #import "NSDictionaryMutableDeepCopy.h"


    @implementation NSDictionary(MutableDeepCopy)

    - (NSMutableDictionary *)mutableDeepCopy {
        NSMutableDictionary *ret = [[NSMutableDictionary alloc]
                                    initWithCapacity:[self count]];
        NSArray *keys = [self allKeys];
        for (id key in keys) {
            id oneValue = [self valueForKey:key];
            id oneCopy = nil;
            
            if ([oneValue respondsToSelector:@selector(mutableDeepCopy)]) {
                oneCopy = [oneValue mutableDeepCopy];
            }
            else if ([oneValue respondsToSelector:@selector(mutableCopy)]) {
                oneCopy = [oneValue mutableCopy];
            }
            if (oneCopy == nil) {
                oneCopy = [oneValue copy];
            }
            [ret setValue:oneCopy forKey:key];
        }
        
        return ret;
    }

    @end

    使用类别方法来实现。

    自定义对象

    如果是我们定义的对象,那么我们自己要实现NSCopying,NSMutableCopying这样就能调用copy和mutablecopy了。举个例子:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    5
    @interface MyObj : NSObject<nscopying ,NSMutableCopying>
    {
             NSMutableString *name;
             NSString *imutableStr;
             int age;
    }
    @property (nonatomic, retain) NSMutableString *name;
    @property (nonatomic, retain) NSString *imutableStr;
    @property (nonatomic) int age;
     
    @end
     
    @implementation MyObj
    @synthesize name;
    @synthesize age;
    @synthesize imutableStr;
    - (id)init
    {
             if (self = [super init])
             {
                       self.name = [[NSMutableString alloc]init];
                       self.imutableStr = [[NSString alloc]init];
                       age = -1;
             }
             return self;
    }
     
    - (void)dealloc
    {
             [name release];
             [imutableStr release];
             [super dealloc];
    }
    - (id)copyWithZone:(NSZone *)zone
    {
             MyObj *copy = [[[self class] allocWithZone:zone] init];
             copy->name = [name copy];
             copy->imutableStr = [imutableStr copy];
    //       copy->name = [name copyWithZone:zone];;
    //       copy->imutableStr = [name copyWithZone:zone];//
             copy->age = age;
     
             return copy;
    }
    - (id)mutableCopyWithZone:(NSZone *)zone
    {
             MyObj *copy = NSCopyObject(self, 0, zone);
             copy->name = [self.name mutableCopy];
             copy->age = age;
             return copy;
    }
    @end
    </nscopying

    说法二

    首先关于copy和mutableCopy的行为:不管是NSString这种元素类、还是NSArray这样的容器类、还是Mutable和非Mutable类,copy和mutableCopy调用后表现的行为到底是什么样完成取决于类本身NSCopying和NSMutableCopying协议是如何实现的。

    想要正常调用copy和mutableCopy两个函数,那么类就一定要实现对应的协议。

    1.      元素数据的copy和mutableCopy。

    常用的NSString类,示例代码如下:

    1. NSString* string = @”a”;  
    2. NSString* stringCopy = [string copy];// stringCopy与string地址相同,retainCount+ 1  
    3. NSMutableString* stringMCopy = [string mutablecopy];// stringMCopy与string地址不同  
    4.    
    5. NSMutableString* stringM1 = [stringMCopy copy];//地址与stringMCopy不同,且为不可修改  
    6. NSMutableString* stringM2 = [stringMCopy mutablecopy];//地址与stringMCopy不同,可修改   

    可以基本推出NSString和NSMutableString中两个协议的实现

     
    1. NSString:  
    2. - (id)copywithZone:(NSZone*)zone  
    3. {  
    4.   return self;  
    5. }  
    6.    
    7. - (id)mutableCopywithZone:(NSZone*)zone  
    8. {  
    9.   NSMutableString* copy =[[NSMutableString alloc] initxxxxxx];  
    10.   ....  
    11.   return copy;  
    12. }  
    13. NSMutableString:  
    14. - (id)copywithZone:(NSZone*)zone  
    15. {  
    16.   NSString* copy = [[NSStringalloc] initxxxxxx];  
    17.   ....  
    18.   return copy;//所以不可修改  
    19. }  
    20.    
    21. - (id)mutableCopywithZone:(NSZone*)zone  
    22. {  
    23.   NSMutableString* copy =[[NSMutableString alloc] initxxxxxx];  
    24.   ....  
    25.   return copy;  
    26. }  

    2.      容器类的copy和mutableCopy。

    常用类NSArray和NSMutableArray,看如下示例代码:


    1. Class1* obj1= ....;//正常初始化  
    2. NSArray* array = [[NSArray alloc] initWithObjects:obj1, nil];  
    3. NSArray* arrayCopy = [array copy];//地址不变,retaincount+1  
    4. NSMutableArray* arrayMCopy = [array mutableCopy];//地址改变,但是数组中成员指针和obj1相同,浅拷贝  
    5.   
    6. NSMutableArray* arrayM1 = [arrayMCopy Copy];//地址改变,但是数组中成员指针和obj1相同,浅拷贝。arrayM1为NSArray不可修改  
    7. NSMutableArray* arrayM2 = [arrayMCopy mutableCopy];//地址改变,但是数组中成员指针和obj1相同,浅拷贝  

    1. //推断  

    1. NSArray:  
    2. - (id)copywithZone:(NSZone*)zone  
    3. {  
    4.   //伪码  
    5.   return [self retain];  
    6. }  
    7.   
    8. - (id)mutableCopywithZone:(NSZone*)zone  
    9. {  
    10.   NSMutableArray* copy = [[NSMutableString alloc] initxxxxxx];  
    11.   for (id element in self) {  
    12.     [copy addObject:element];//element retian count + 1  
    13.     ....  
    14.   }  
    15.   return copy;  
    16. }  
    17.   
    18. NSMutableArray:  
    19. - (id)copywithZone:(NSZone*)zone  
    20. {  
    21.   NSArray* copy = [[NSArray alloc] initXXX];  
    22.   /*把每个element加入到copy数组,retainCount+1*/  
    23.   ....  
    24.   return copy;  
    25. }  
    26.   
    27. - (id)mutableCopywithZone:(NSZone*)zone  
    28. {  
    29.   NSMutableArray* copy = [[NSMutableString alloc] initxxxxxx];  
    30.   for (id element in self) {  
    31.     [copy addObject:element];//element retian count + 1  
    32.     ....  
    33.   }  
    34.   return copy;  
    35. }  

    3.      深拷贝

    上面提到的官方文档中介绍两种实现深拷贝的方法:

    a.      用Array的initWithArray:  copyItems函数,如下:

    NSArray *deepCopyArray=[[NSArray alloc] initWithArray: someArraycopyItems: YES];

    调用后,会对原NSArray中的每个元素调用其copy函数,并把返回的id加入到新的数组中。所以这是依赖于Obj对象类实现的深拷贝,如果- (id)copywithZone:(NSZone*)zone是重新分配一块内存赋值后返回,那么就是真正的深拷贝。如果直接返回自身,那么它只是浅拷贝。

    b.      用archiver方式:

    NSArray* trueDeepCopyArray = [NSKeyedUnarchiverunarchiveObjectWithData: [NSKeyedArchiver archivedDataWithRootObject:oldArray]];

    这是真正意义上的深拷贝,不依赖于实际类Copying协议的实现。

    4. 用Category实现自定义的深拷贝deepmutableCopy,如:


      1. - (NSMutableArray *)mutableDeepCopy  
      2. {  
      3.     NSMutableArray *ret = [[NSMutableArrayalloc] initWithCapacity:[self count]];  
      4.     for (id value in self)  
      5.     {  
      6.         id oneCopy = nil;  
      7.         if ([value respondsToSelector:@selector(mutableDeepCopy)])  
      8.             oneCopy = [value mutableDeepCopy];  
      9.         else if ([value respondsToSelector:@selector(mutableCopy)])  
      10.             oneCopy = [value mutableCopy];  
      11.         if (oneCopy == nil)  
      12.             oneCopy = [value copy];  
      13.         [ret addObject: oneCopy];  
      14.     }  
      15.     return ret;  
  • 相关阅读:
    CodeForces gym Nasta Rabbara lct
    bzoj 4025 二分图 lct
    CodeForces 785E Anton and Permutation
    bzoj 3669 魔法森林
    模板汇总——快读 fread
    bzoj2049 Cave 洞穴勘测 lct
    bzoj 2002 弹飞绵羊 lct裸题
    HDU 6394 Tree 分块 || lct
    HDU 6364 Ringland
    nyoj221_Tree_subsequent_traversal
  • 原文地址:https://www.cnblogs.com/isItOk/p/5433009.html
Copyright © 2011-2022 走看看