얕은 복사와 깊은 복사를 이야기하다

15497 단어
왜 이런 기초 지식을 뒤돌아보느냐, 왜냐하면 기억력이 썩은 붓끝보다 못하다고 생각하기 때문이다.예전에는 그 까닭을 알지 못했던 것이 기능을 실현하기 위해 기능을 실현하기 위해 귀납하고 정리할 시간이 없었기 때문에 문장의 형식으로 기록하는 것이 좋다고 생각했다. 자, 잡담을 많이 하지 않고 본론으로 돌아가자. 물론 같은 종류의 문장이 비일비재하다. 그러나 자신이 한 번 쓰는 것과 한 번 보는 것은 느낌이 다르다. 어쨌든 자신의 체험 중의 하나라고 할 수 있다.
복제는 복제 대상이다.카피는 얕은 카피, 깊은 카피 두 종류로 나뉜다.
  • 얕은 복사는 새로운 대상을 만들지 않습니다. 바늘이 가리키는 주소가 바뀌지 않았습니다. 즉, 새로운 메모리 공간을 만들지 않았습니다. 즉 복사 바늘입니다.
  • 깊이 복사는 새로운 대상을 만들고 바늘이 가리키는 주소가 바뀐다. 즉, 새로운 메모리 공간, 즉 내용 복사를 만들었다.

  • 그럼 copy와 mutable Copy는 어떤 차이가 있을까요?
  • copy 변경 불가 사본, 변경 불가 Foundation 프레임워크의 일반 클래스에는 새 메모리 공간을 만들지 않으며, 변경 가능한 Foundation 프레임워크의 일반 클래스와 사용자 정의 객체에는 새 객체에 메모리 공간을 개척합니다.새 객체는 변경될 수 없습니다.
  • mutable Copy 가변 복사는 메모리에 영역을 다시 열고 대상을 복사해서 이 영역에 놓는 것이다.새로운 대상은 바꿀 수 있고 새로운 대상의 변화는 원래 대상에게 영향을 주지 않는다.

  • 예를 들어 다음과 같이 설명합니다.
    1. NSString
    불변 문자열
     NSString *str = @"test";
     NSString *copyStr = [string copy];
     NSMutableString *mutableCopyStr = [string mutableCopy];
     
     NSLog(@"str:%@,%p",str,str);
     NSLog(@"copyStr:%@,%p,%d",copyStr,copyStr,copyStr == str);
     NSLog(@"mutableCopyStr:%@,%p,%d",muCopyStr,mutableCopyStr,mutableCopyStr == str);
     
    
     :
    
    str:test,0x10145b098
    
    copyStr:test,0x10145b098,1
    
    muCopyStr:test,0x7feb9355aed0,0
    

    가변 문자열
    NSMutableString  *str = [[NSMutableString alloc]initWithString:@"test"];
    NSString *copyStr = [str copy];
    NSMutableString *mutableCopyStr = [str mutableCopy];
    
    NSLog(@"str:%@,%p",str,str);
    NSLog(@"copyStr:%@,%p,%d",copyStr,copyStr,copyStr == str);
    NSLog(@"mutableCopyStr:%@,%p,%d",mutableCopyStr,mutableCopyStr,mutableCopyStr == str);
    
     :
    
    str:test,0x7fbd90f0f640
    
    copyStr:test,0xa000000747365744,0
     
    muCopyStr:test,0x7fbd90f0be70,0
    

    이를 통해 알 수 있듯이 변하지 않는 문자열에 대해copy는 새로운 대상을 만들지 않고mutableCopy는 새로운 대상을 만들 수 있다.가변 문자열에 대해 copy와mutableCopy는 모두 새로운 대상을 생성합니다. 다만 copy는 변할 수 없는 새로운 대상을 생성하고mutableCopy는 변할 수 있는 새로운 대상을 생성합니다.
    이 예를 다시 한 번 보도록 하겠습니다.
    NSString  *str =@"test"; 
    NSString *copyStr = [str copy];
    NSMutableString *mutableCopyStr = [str mutableCopy];
       
    str = @"test0";
    // Str , Str , 
        
    NSLog(@"str:%@,%p",str,str);
    NSLog(@"copyStr:%@,%p,%d",copyStr,copyStr,copyStr == str);
    NSLog(@"mutableCopyStr:%@,%p,%d",mutableCopyStr,mutableCopyStr,mutableCopyStr == str);
    
    str:test0,0x10745e218
    copyStr:test,0x10745e098,0
    mutableCopyStr:test,0x7fa15beab330,0
    

    NSArray
    NSArray와 NSstring은 모두 Foundation 클래스의 하나이기 때문에 상황은 모두 같다
    불변 배열
    NSArray  *arr =@[@"test"];
    NSArray *copyArr = [arr copy];
    NSMutableArray *mutableCopyArr = [arr mutableCopy];
    
    NSLog(@"arr:%@,%p",arr,arr);
    NSLog(@"copyArr:%@,%p,%d",copyArr,copyArr,copyArr == arr);
    NSLog(@"mutableCopyArr:%@,%p,%d",mutableCopyArr,mutableCopyArr,mutableCopyArr == arr);
    
     :
    arr:(
        test
    ),0x7fdc4161bbd0
    
    copyArr:(
        test
    ),0x7fdc4161bbd0,1
    
    mutableCopyArr:(
        test
    ),0x7fdc4161b160,0
    

    가변 배열
    NSMutableArray *arr = [NSMutableArray arrayWithObjects:@"1",@"2",@"3",nil];
    NSMutableArray *copyArr = [arr copy];
        
    [copyArr removeLastObject];
    
    Xcode 
    
    *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSArrayI removeLastObject]: unrecognized selector sent to instance 0x7fa2da614120'
    
    

    Foundation 프레임워크에서 자주 사용하는 종류는 NSNumber, NSString, NSArray, NSDictionary, NSMutableArray, NSMutableDictionay, NSMutableString 등copy가 생성한 대상은 변할 수 없고,mutableCopy가 생성한 대상은 변할 수 있는 것이다.
    
    NSMutableArray *arr = [NSMutableArray arrayWithObjects:@"1",@"2",@"3",nil];
    NSArray *copyArr = [arr copy];
      
    NSMutableArray *mutableCopyArr = [arr mutableCopy];
        
    arr[0] = @"0";
        
    NSLog(@"arr:%@,%p",arr,arr);
    NSLog(@"copyArr:%@,%p,%d",copyArr,copyArr,copyArr == arr);
    NSLog(@"mutableCopyArr:%@,%p,%d",mutableCopyArr,mutableCopyArr,mutableCopyArr == arr);
        
    [mutableCopyArr addObject:@"hello"];
         
    NSLog(@"mutableCopyArr:%@,%p,%d",mutableCopyArr,mutableCopyArr,mutableCopyArr == arr);
    
    
     :
    arr:(
        0,
        2,
        3
    ),0x7f94fbe12550
    
    copyArr:(
        1,
        2,
        3
    ),0x7ffc12611020,0
    
     mutableCopyArr:(
        1,
        2,
        3
    ),0x7f94fbe79f80,0
    

    Foundation 프레임워크의 일반 클래스에 대해 다음을 수행합니다.
  • 변경 불가: (NS*)
  • copy, Sheet Copy, 신규 객체 없음
  • mutable Copy, 딥 카피, 새로운 가변 객체 생성
  • 가변: (NSMutable*)
  • copy, 딥 카피, 새로운 불변 객체 생성,
  • mutable Copy, 딥 카피, 새로운 가변 객체 생성

  • 또 하나 말하자면, 우리는 서면량 문법으로 이런 종류를 많이 만드는 것을 비교적 제창한다
    예를 들면 다음과 같습니다.
    NSMutableArray *arr = [@[] mutableCopy];
    
    NSMutableDictionary *dict = [@{} mutableCopy];
    

    3. 사용자 정의 대상
    일단 Person 클래스를 만들겠습니다.
    @interface Person : NSObject
    @property (nonatomic, copy)NSString *pid;
    @property (nonatomic, copy)NSString *name;
    @end
    
    // Person 
    Person *p = [[Person alloc] init];
    p.name = @"joe";
    p.pid = @"10";
    
    Person *copyP = [p copy];
    

    Xcode가 실행되면 예상치 못한 오류가 발생합니다.
    *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[Person copyWithZone:]: unrecognized selector sent to instance 0x7ff643641780'
    
    

    왜냐하면 copyWithZone:이 방법이 실현되지 않았기 때문이다.
    @protocol NSCopying
    - (id)copyWithZone:(nullable NSZone *)zone;
    @end
    
    @protocol NSMutableCopying
    - (id)mutableCopyWithZone:(nullable NSZone *)zone;
    @end
    

    command+왼쪽 단추를 누르면 이 방법이 NSCopying의 프로토콜 방법임을 알 수 있습니다. 그러면 Person은 NSCopying 방법을 먼저 따라야 합니다.
    @interface Person : NSObject
    @property (nonatomic, copy)NSString *pid;
    @property (nonatomic, copy)NSString *name;
    @end
    
    @implementation Person
    - (id)copyWithZone:(NSZone *)zone{
        
        return @"test";
    }
    
    // Person 
    
    Person *p = [[Person alloc] init];
    p.name = @"joe";
    p.pid = @"10";
    
    Person *copyP = [p copy];
    
    NSLog(@"p = %p copyP = %p", p, copyP);
    
    p = 0x7fab996acf50 copyP = 0x10ab5c088
    

    결과적으로 copyWithZone: (NSZone*)zone은 실제적으로 새로운 대상에게 메모리 공간을 재분배했고 반환은 문자열이기 때문에 copyP는 실제적으로 NSString 클래스의 대상이다
    @implementation Person
    - (id)copyWithZone:(NSZone *)zone{
       // p zone, alloc 
       // Person *p = [[Person alloc]init];
       // Person , Person allocWithZone  [self class]  allocWithZone:
       // Person *p = [[Person allocWithZone:zone]init];
        Person *p = [[[self class] allocWithZone:zone] init];
        p.name = self.name;
        p.pid = self.pid;
        return p;
    }
    
    // Person 
    Person *p = [[Person alloc] init];
    p.name = @"joe";
    p.pid = @"10";
     
    Person *copyP = [p copy];
    
    NSLog(@"p = %p copyP = %p", p, copyP);
    NSLog(@"name = %@,  pid = %@", copyP.name, copyP.pid);
        
    NSLog(@"p.name :%p,copyP.name :%p",p.name,copyP.name);
    
    
    p = 0x7fbfcb40e470 copyP = 0x7fbfcb418330
    
    name = joe,  pid = 10
    
    p.name :0x10489d2a8,copyP.name :0x10489d2a8
    
    copyWithZone은 얕은 복사본이고 사용자 정의 대상은 이 방법을 다시 쓴다.copy를 진행할 때 새로운 대상에게 새로운 메모리 주소를 분배하고 mutableCopyWithZone은 깊은 복사본이고 새로운 대상에게 새로운 메모리 주소를 분배하기 때문에 [p copy]을 할 때 NSCopying 프로토콜을 따라 copyWithZone을 다시 쓰는 방법, [p mutableCopy]NSMutableCopying으로 할 때 mutableCopyWithZone을 따라 다시 쓰는 방법도 마찬가지다.
    - (id)mutableCopyWithZone:(NSZone *)zone{
       Person *p = [[[self class] allocWithZone:zone] init];
       p.name = self.name;
       p.pid = self.pid;
       return p;
    }
    

    하위 클래스가 상위 클래스를 상속할 때 하위 클래스도 상위 NSCopying 프로토콜을 따른다. 이 때 하위 클래스에서 copyWithZone 방법을 다시 쓰지 않아도 된다.그러나 부류에서runtime를 사용하여 모든 구성원 속성 목록을 훑어보고 값을 부여할 수 있지만 부류의 속성은 값을 부여하지 못합니다.
    @interface Person : NSObject
    @property (nonatomic, copy)NSString *pid;
    @property (nonatomic, copy)NSString *name;
    @end
    
    @implementation Person
    - (id)copyWithZone:(NSZone *)zone{
       id obj = [[[self class]allocWithZone:zone]init];
       unsigned int outCount;
       Ivar * ivars = class_copyIvarList([self class], &outCount);
       for (int i = 0; i < outCount; i ++) {
           Ivar ivar = ivars[i];
           NSString * key = [NSString stringWithUTF8String:ivar_getName(ivar)];
           id value = [self valueForKey:key];
           [obj setValue:value forKey:key];
        }
       free(ivars);
       return obj;
    }
    
    @interface Student : Person
    @property (nonatomic, copy) NSString *age;
    @end
    
    // Student 
    Student *s = [[Student alloc]init];
    s.name = @"marry";
    s.age = @"18";
        
    Student *copyS = [s copy];
    NSLog(@"s = %p copyS = %p", s, copyS);
    NSLog(@"name = %@ age = %@", copyS.name, copyS.age);
    
    
     :
    
    s = 0x7fa83a608ce0 copyS = 0x7fa83a61a100
    
    name = (null)  age = 18
    

    이럴 때 속성을 보면 스트롱과 코피가 이해하기 쉬워요. 예를 들면 저희가 Person 클래스에 속성을 하나 더 넣었어요.
    @interface Person : NSObject
    @property (nonatomic, copy)NSString *pid;
    @property (nonatomic, copy)NSString *name;
    @property (nonatomic, copy) NSMutableArray *arr;
    @end
    
    // Person 
    
     Person *p = [[Person alloc] init];
     p.name = @"joe";
     p.pid = @"10";
     NSMutableArray *arr =  [@[@"test"] mutableCopy];
     p.arr = arr;
        
     [arr addObject:@"hell0"];
    
     Person *copyP = [p copy];
        
        
    NSLog(@"arr:%p, p.arr:%@  %p,
    copyP.arr:%@ %p",arr,p.sign,p.arr,copyP.arr,copyP.arr);
    arr:0x7fddea4358a0,p.arr:(
        test
    ),0x7fddea43d0b0,
    
    copyP.arr:(
        test
    ),0x7fddea43d0b0
    

    @property 본질: ivar (실례 변수), 접근 방법 (access method = getter + setter)
    copy로 수식할 때,self.arr 실제 실현 이 setter 방법
    - (void)setArr:(NSMutableArray *)arr{
        _arr = [arr copy];
    }
    
    [p.arr addObject:@"hell0"];
    
     
    [__NSArrayI addObject:]: unrecognized selector sent to instance 0x7ff3b94ee1a0'
    

    그래서 셀프.arr는 여전히 변할 수 없습니다. @property에 변할 수 있는 속성을 추가하지 마십시오.
    그러면 스트롱으로 한번 꾸며볼게요.
    @property (nonatomic, strong) NSMutableArray *arr;
    
    // Person 
    
    Person *p = [[Person alloc] init];
    p.name = @"joe";
    p.pid = @"10";
    NSMutableArray *arr =  [@[@"test"] mutableCopy];
    p.arr = arr;
        
    [arr addObject:@"hell0"];
    
    Person *copyP = [p copy];
    
    NSLog(@"arr:%p, p.arr:%@ %p,
    copyP.arr:%@ %p",arr,p.sign,p.arr,copyP.arr,copyP.arr);
    arr:0x7fbbf8503cc0,p.arr:(
        test,
        hell0
    ),0x7fbbf8503cc0,
    
    copyP.arr:(
        test,
        hell0
    ),0x7fbbf8503cc0
    
    

    실제적으로strong으로 수식할 때,self.arr 이 setter 방법을 실현합니다
    - (void)setArr:(NSMutableArray *)arr{
        _arr = arr
    }
    

    그래서strong으로 수식할 때,self.arr는 가변 수조로 주소가 변경되지 않았습니다. arr가 바뀔 때self.arr도 바뀔 거야.
    위에서 알 수 있듯이 copyWithZone: 얕은 복제가 발생하기 때문에 이런 방법은 1층 깊이 복제만 발생할 수 있다. 만약에 집합 안의 원소가 여전히 집합이라면 하위 집합 안의 원소는 깊이 복제되지 않고 하위 집합 안의 원소 바늘만 복제한다.
    이 예를 한번 봅시다.
    NSMutableString * testStr = [NSMutableString stringWithString:@"test"];
    
    NSArray *arr = @[testStr];
    NSArray *copyArr = [arr copy];
    NSMutableArray *mutableCopyArr = [arr mutableCopy];
    NSArray *arr2 = [[NSArray alloc]initWithArray:arr copyItems:YES];
        
    [testStr appendString:@" hello"];
        
     NSLog(@" arr.firstObject = %p, 
    copyArr.firstObject= %p,
    mutableCopyArr.firstObject= %p,
    arr2.firstObject =%p",arr.firstObject, copyArr.firstObject, mutableCopyArr.firstObject, arr2.firstObject); NSLog(@"arr %@,
    copyArr %@,
    mutableCopyArr %@,
    arr2 %@",arr, copyArr, mutableCopyArr, arr2);
     :
    
    arr.firstObject = 0x7fbfd27069a0, 
    copyArr.firstObject = 0x7fbfd27069a0, 
    mutableCopyArr.firstObject =  0x7fbfd27069a0, 
    arr2.firstObject = 0xa000000747365744
     
    arr (
        "test hello"
    ), 
     copyArr (
        "test hello"
    ), 
     mutableCopyArr (
        "test hello"
    ), 
     arr2 (
        test
    )
    

    initWithArray:copyItems:YES, initWithDictionary copyItems:YES 사용
    Tier 2 에만 복제할 수 있으며, 더 깊은 부분이 있으면 완전히 복제할 수 없습니다.
    NSMutableString * testStr = [NSMutableString stringWithString:@"test"];
    NSMutableArray *testArr = [NSMutableArray arrayWithObject:testStr];
    
    NSArray *arr = @[testArr];
    NSArray *copyArr = [arr copy];
    NSMutableArray *mutableCopyArr = [arr mutableCopy];
    NSArray *arr2 = [[NSArray alloc]initWithArray:arr copyItems:YES];
        
    [testStr appendString:@" hello"];
        
    NSLog(@" arr.firstObject = %p, 
    copyArr.firstObject= %p,
    mutableCopyArr.firstObject =%p,
    arr2.firstObject =%p",arr.firstObject, copyArr.firstObject, mutableCopyArr.firstObject, arr2.firstObject); NSLog(@"arr %@,
    copyArr %@,
    mutableCopyArr %@,
    arr2 %@",arr, copyArr, mutableCopyArr, arr2);
     :
    arr.firstObject = 0x7fb50bf0cf60, 
    copyArr.firstObject = 0x7fb50bf0cf60, 
    mutableCopyArr.firstObject = 0x7fb50bf0cf60, 
    arr2.firstObject = 0x7fb50bf002f0
     
    arr (
            (
            "test hello"
        )
    ), 
     copyArr (
            (
            "test hello"
        )
    ), 
     mutableCopyArr (
            (
            "test hello"
        )
    ), 
     arr2 (
            (
            "test hello"
        )
    )
    
    

    완전 심도 복제를 실현하려면 압축 파일을 사용해서 해제해야 한다.아카이브와 아카이브를 사용하려면 NSCoding 프로토콜을 준수해야 합니다.
    NSMutableString * testStr = [NSMutableString stringWithString:@"test"];
    NSMutableArray *testArr = [NSMutableArray arrayWithObject:testStr];
        
    NSArray *arr = @[testArr];
    NSArray *copyArr = [arr copy];
    NSMutableArray *mutableCopyArr = [arr mutableCopy];
    NSArray *arr2 = [[NSArray alloc]initWithArray:arr copyItems:YES];
        
     // 
        
    NSArray *arr3 = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:arr]];
        
    [testStr appendString:@" hello"];
        
    id  firstObject1 = [arr firstObject][0];
    id  firstObject2 = [copyArr firstObject][0];
    id  firstObject3 = [mutableCopyArr firstObject][0];
    id  firstObject4 = [arr2 firstObject][0];
    id  firstObject5 = [arr3 firstObject][0];
        
    NSLog(@" arr.firstObject.firstObject = %p, 
    copyArr.firstObject.firstObject = %p,
    mutableCopyArr.firstObject.firstObject = %p,
    arr2.firstObject.firstObject = %p,arr3.firstObject.firstObject = %p",firstObject1, firstObject2, firstObject3, firstObject4,firstObject5); NSLog(@"arr %@,
    copyArr %@,
    mutableCopyArr %@,
    arr2 %@,
    arr3 %@",arr, copyArr, mutableCopyArr,arr2,arr3);
     :
    
    arr.firstObject.firstObject = 0x7fd4d271e8d0, copyArr.firstObject.firstObject = 0x7fd4d271e8d0, 
    mutableCopyArr.firstObject.firstObject = 0x7fd4d271e8d0, 
    arr2.firstObject.firstObject = 0x7fd4d271e8d0,
    arr3.firstObject.firstObject = 0x7fd4d272b360
     
    arr (
            (
            "test hello"
        )
    ), 
    copyArr (
            (
            "test hello"
        )
    ), 
    mutableCopyArr (
            (
            "test hello"
        )
    ), 
    arr2 (
            (
            "test hello"
        )
    ),
    arr3 (
            (
            test
        )
    )
    
    

    우리는 깊이 있는 복사를 고려할 때 집합된 구조 요소가 다른 집합을 끼워 넣지 않으면mutableCopy는 모든 내용을 복제합니다. 집합 요소가 집합이든 집합이든 압축 파일의 해제를 고려하여 완전한 깊이 복사를 실현해야 합니다.

    좋은 웹페이지 즐겨찾기