Deep Copy

copymutableCopy를 사용하면 객체의 사본을 만들 수 있다. 이는 같은 데이터를 가리키지 않고 별도의 할당된 메모리에 데이터를 복사하여 또 하나의 할당된 객체를 만들어주는 메소드이다. 통상 NSCopying 프로토콜을 따르면 복사가 가능하고, 그 중 변경이 가능한 mutable한 객체들은 mutableCopy를 제공한다.

문제는 어떤 객체가 다른 객체를 가리키고 있는 경우에는 포인터만 복사된다는 것이다. 예를 들어서 NSArrayNSDictionary의 경우, 배열이나 사전 객체를 복사하면 하나의 사전, 배열이 추가로 생성되는 것은 맞다. 하지만 배열의 모든 원소는 원본 배열의 원소와 같은 포인터가 복사되는 것으로, 배열 그 자체는 사본이지만, 그 속에 담긴 객체들은 모두 원본인 셈이다. 이 경우 사본에서 특정 원소를 파괴하면 원본의 배열은 해제된 메모리를 참조하므로 문제가 발생한다.

NSObject 자체는 NSCopying 프로토콜을 따르지 않는다. 따라서 NSCopying을 따르지 않는 원소(커스텀 클래스)가 배열에 포함될 가능성은 매우 높으므로 배열은 원소의 포인터만을 복사한다.

따라서 만약 배열의 원소가 (배열을 root object로 하는 객체 그래프 상의 모든 객체가) 복사가 가능하다면 깊은 복사를 통해서 완전한 사본을 만들 수 있다. 다음은 카테고리를 사용해서 NSArray에 깊은 복사를 하고 있다.

  1. 각각의 원소가 깊은 복사가 가능하면 깊은 복사를 한다.
  2. 각각의 원소가 얕은 복사가 가능하면 얕은 복사를 한다.
  3. 복사가 되지 않는 원소는 NSNull 객체로 대체한다.

코드는 다음과 같다.

@interface NSArray (DeepCopy)
-(id)DeepCopy;
@end
@implementation NSArray (DeepCopy)
-(id)DeepCopy
{
    NSMutableArray *copiedArray = [[NSMutableArray alloc] initWithCapacity:[self count]];
    NSArray *result = nil;
    @autoreleasepool {
        for(id item in self) {
            id copiedItem = nil;
            if([item repondsToSelector:@selecotor(deepCopy)]) {
                copiedItem = [item deepCopy];
            } else if ([item repondsToSelector:@selector(copy)]) {
                copiedItem = [item copy];
            }
            if (copiedItem == nil) copiedItem = [NSNull null];
            [copiedArray addObject:copiedItem];
        }
        result = [copiedArray copy];
        [copiedArray autorelease]
    }
    return result;
}

만약 원소들이 모두 인코딩될 수 있는 객체(NSCoding을 지원)라면 다음 방법도 가능하다. 아카이빙하여 데이터로 만든 후, 이 데이터로부터 깔끔하게 복원한 객체(완전한 사본이 된다)를 반환하면 된다.

-(id)deepCopy {
    NSArray *copiedArray = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:self]];
    return copiedArray;
}

Deep Mutable Copy

깊은 뮤터블 복사를 하려면 절차를 하나 더 넣으면 된다. 다음 순서대로 복사를 시도해서 가능한 원소들을 깊은 뮤터블 복사한 NSMutableArray를 만들 수 있다.

  1. 깊은 뮤터블 복사가 가능한 원소는 깊은 뮤터블 복사를 한다.
  2. 깊은 복사가 가능한 원소는 깊은 복사를 한다.
  3. 얕은 복사를 시도한다.
  4. Null로 대체한다.

Read more

워드프레스에서 고스트로 이전

워드프레스에서 고스트로 이전

이 글을 쓰면서도 믿기 힘든 사실인데, 블로그라는 걸 처음 시작한지가 20년이 되었습니다. 이글루스에서 처음 시작했다가, SK컴즈가 인수한다고 발표함과 동시에 워드프레스로 플랫폼을 옮겼죠. 워드프레스오 옮긴 이후에는 호스팅 환경을 이리 저리 옮기긴 했지만 거의 18년 가까이 워드프레스를 사용해온 것 같습니다. 그 동안 워드프레스는 블로깅 툴에서 명실상부한 범용CMS로 발전했습니다. 사실 웬만한 홈페이지들은 이제

By sooop
띄어쓰기에 대한 생각

띄어쓰기에 대한 생각

업무 메일을 쓸 때 가장 많이 쓰는 말 중에 하나가 메일 말미에 ‘업무에 참고 부탁 드립니다.‘인데요, 어느 날부터 아웃룩에서 이 ‘부탁 드립니다’가 틀렸다고 맞춤법 지적을 하기 시작했습니다. 맞는 말은 ‘부탁드립니다’라고 붙여 쓰는 거라고. 사실 아래아한글 시절부터 이전의 MS워드까지, 워드프로세서들의 한국어 맞춤법 검사 실력은 거의 있으나 마나 한

By sooop

구글 포토에서 아이클라우드로 탈출한 후기

한 때 구글 포토가 백업 용량을 무제한으로 제공해 주겠다고해서, 구글 포토를 사용해서 사진을 백업해왔습니다. 물론 이 이야기의 결말은 저나 이 글을 읽고 있는 여러분이나 모두 알고 있습니다. 사실 AI에게 학습 시킬 이미지 데이터를 모으기 위한 것일 뿐이라거나 하는 이야기는 그 당시에도 있었습니다만, 에이 그래도 구글인데 용량은 넉넉하게 주겠지…하는 순진한

By sooop

Julia의 함수 사용팁

연산자의 함수적 표기 Julia의 연산자는 기본적으로 함수이며, 함수 호출 표기와 같은 방식으로 호출하는 것이 가능합니다. 또한 그 자체로 함수이기 때문에 filter(), map() 과 같이 함수를 인자로 받는 함수에도 연산자를 그대로 적용하는 것이 가능합니다. 특히 + 연산자는 sum() 함수와 같이 여러 인자를 받아 인자들의 합을 구할 수 있습니다. 2 + 3 # = 5 +(2,

By sooop