콘텐츠로 건너뛰기
Home » [Cocoa] NSArray 정렬하기

[Cocoa] NSArray 정렬하기

Swft의 Array는 간단한 방법으로 정렬할 수 있지만, Objecitve-C의 배열인 NSArray는 정렬하는 것이 그리 간단치가 않다. 배열을 정렬하기 위해서는 어떤 알고리듬을 사용하는지와 무관하게 기본적으로 두 개의 요소 중에서 어느 것이 앞으로 가야할지, 비교할 수 있는 기준이 필요하다. 두 요소가 특정 기준에 대해 동일한 값을 같는다면 두 번째로 적용되어야 할 기준을 사용하는 경우도 있다.

코코아에서는 NSSortDescriptor라는 클래스를 사용하여 배열(다른 집합형 컨테이너도 해당) 내의 원소들을 정렬할 수 있다. 이 디스크립터를 사용해서 정렬 기준을 만들고 정렬하는 메소드에 파라미터로 전달한다. 단, 이러한 방식은 매우 오래전의 방식이며, Objecitve-C 2.0에서 코드 블럭에 대한 지원이 강화된 뒤로는 코드 블럭이나 함수, 메소드를 사용하여 정렬하는 방식이 더 널리 쓰인다.

참고로 NSArray는 변경이 불가능한 immutable한 타입이다. 이러한 타입들은 정렬 시 sortedArray~*와 같은 식으로 정렬된 사본을 만든다. NSMuatbleArray는 내부 변경이 가능하므로, -sortWith*: 와 같은 메소드도 사용할 수 있다.

정렬 디스크립터

정렬 디스크립터는 어떤 배열/집합 내 원소들에서 그 중 어떤 속성을 사용하여 순서를 정할 것인지를 정의하는 객체이다. 따라서 비교에 사용할 기준이 되는 프로퍼티 이름을 키로 사용하여 간단하게 생성할 수 있다.

하지만 어떤 경우에는 프로퍼티 자체로 비교가 곤란하거나, 별도의 로직으로 비교하고 싶은 경우도 있다. 대소문자를 구분하지 않는 문자열 비교나, 문자열의 길이 순서대로 정렬하는 등 별도의 로직을 이용하려는 경우에는 코드 블럭을 사용하여 비교하는 디스크립터를 만들면된다.

NSSortDescriptor *ageDescriptor = [NSSortDescriptor descriptorWithKey:@"age" 
                                   ascending:YES];
NSSortDescriptor *hireDateDescriptor = [NSSortDescriptor 
                     descriptorWithKey:@"hireDate"
                     asceding:NO];

NSArray *sortedEmployees = [employeesArray sortedArrayUsingDescriptors:
                            @[ageDescriptor, hireDateDescriptor]];

블럭을 사용하여 정렬하기

-sortedArrayUsingComparator: 를 사용하면 디스크립터 대신에 별도의 코드 블럭을 사용하여 배열을 정렬할 수 있다. 이 때 사용되는 코드 블럭은 ^(NSComparisonResult)(id, id) 타입으로 두 개의 객체를 인자로 받고 NSComparisonResult 타입의 값을 리턴한다. 이 값은 앞쪽 인자의 객체가 더 낮은 우선순위인지 높은 순위인지 혹은 같은지를 뜻하는 값으로 다음과 같은 상수로 정의되어 있다.

  • NSOrderedDescending
  • NSOrderedAscending
  • NSOrderedSame
NSArray *sortedEmployees = [employeesArray sortedArrayUsingComparator:^(id obj1, id obj2){
                            if (obj1.age < obj2.age) return NSOrderedAscending;
                            if (obj1.age > obj2.age) return NSOrderedDescending;
                            if ([obj1.hireDate compare:obj2.hireDate] == NSOrderedAscending) {
                              return NSOrderedDescending;
                            } else { return NSOrderedDescending }
                           }];

커스텀 집합을 정렬하기 위한 조건

정렬 디스크립터는 -compareObject:toObject: 메소드를 지원하며, 이를 사용하여 두 객체의 우선순위를 비교할 수 있다. 이를 이용하면 커스텀 집합 클래스의 정렬을 직접 구현할 수 있다. 이 때 정렬 디스크립터는 실제로 비교할 값에 접근하기 위해 비교 대상인 두 객체에 대해서 키-밸류 코딩을 사용하여 프로퍼티에 접근한다. 따라서 집합의 원소가 되는 객체들은 기본적으로 키-밸류 코딩에 호환되는 방식으로 작성되어 있어야 한다.