Другое

Как сортировать NSMutableArray с пользовательскими объектами в Objective-C

Узнайте три эффективных метода сортировки NSMutableArray, содержащей пользовательские объекты, по свойствам NSDate в Objective-C. Сравните подходы с использованием compare:, NSSortDescriptor и блоков с полными примерами кода.

Как отсортировать NSMutableArray, содержащий пользовательские объекты в Objective-C?

У меня есть NSMutableArray объектов Person, и мне нужно отсортировать их по свойству birthDate, которое является NSDate. Я видел упоминания о методе sortedArrayUsingSelector, но я не уверен, как реализовать правильный селектор для этого случая.

В Java я бы обычно реализовывал интерфейс Comparable или использовал Collections.sort с пользовательским компаратором. Какой эквивалентный подход существует в Objective-C для сортировки пользовательских объектов по определенному свойству?

Сортировка NSMutableArray с кастомными объектами в Objective-C

Для сортировки NSMutableArray, содержащего кастомные объекты в Objective-C, существует три основных подхода: реализовать метод compare: в вашем кастомном классе, использовать NSSortDescriptor с Key-Value Coding или использовать метод sortedArrayUsingComparator: с блоком. Для сортировки объектов Person по birthDate (свойству NSDate) подход с методом compare: наиболее похож на интерфейс Comparable в Java, в то время как NSSortDescriptor предлагает большую гибкость для сложных сценариев сортировки.

Содержание

Реализация метода compare:

Наиболее прямой подход к сортировке кастомных объектов в Objective-C - это реализация метода compare: в вашем кастомном классе. Этот метод похож на метод compareTo в Java и позволяет сравнивать объекты напрямую.

Сначала добавьте метод compare: в реализацию класса Person:

objectivec
// Person.m
- (NSComparisonResult)compare:(Person *)otherObject {
    return [self.birthDate compare:otherObject.birthDate];
}

Затем вы можете отсортировать массив с помощью метода sortedArrayUsingSelector::

objectivec
NSArray *sortedArray = [personArray sortedArrayUsingSelector:@selector(compare:)];

Этот подход имеет несколько преимуществ:

  • Он чистый и инкапсулирован внутри класса
  • Он следует естественному порядку сортировки (по возрастанию по умолчанию)
  • Он похож на паттерн интерфейса Comparable в Java

Однако у этого подхода есть ограничения:

  • Вы можете иметь только один метод compare: на класс
  • Он не позволяет использовать разные критерии сортировки (например, по убыванию)
  • Изменения в логике сортировки требуют изменения реализации класса

Примечание: Метод compare: возвращает значение перечисления NSComparisonResult: NSOrderedAscending, NSOrderedSame или NSOrderedDescending.

Использование NSSortDescriptor

NSSortDescriptor предоставляет более гибкий подход к сортировке массивов кастомных объектов с использованием Key-Value Coding (KVC). Это особенно полезно, когда вам нужно сортировать по нескольким критериям или динамически изменять порядок сортировки.

Для сортировки по birthDate по возрастанию:

objectivec
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] 
    initWithKey:@"birthDate" ascending:YES];
NSArray *sortedArray = [personArray sortedArrayUsingDescriptors:@[sortDescriptor]];

Для сортировки по убыванию:

objectivec
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] 
    initWithKey:@"birthDate" ascending:NO];
NSArray *sortedArray = [personArray sortedArrayUsingDescriptors:@[sortDescriptor]];

Вы также можете сортировать по нескольким критериям:

objectivec
NSSortDescriptor *sortByDate = [[NSSortDescriptor alloc] 
    initWithKey:@"birthDate" ascending:YES];
NSSortDescriptor *sortByName = [[NSSortDescriptor alloc] 
    initWithKey:@"name" ascending:YES];
NSArray *sortDescriptors = @[sortByDate, sortByName];
NSArray *sortedArray = [personArray sortedArrayUsingDescriptors:sortDescriptors];

Согласно Учебнику по языку Objective-C, этот подход relies на KVC и может быть немного медленнее, чем прямые вызовы методов.

Использование sortedArrayUsingComparator:

Этот современный подход использует блок для определения логики сравнения, предлагая гибкость без изменения ваших кастомных классов:

objectivec
NSArray *sortedArray = [personArray sortedArrayUsingComparator:^NSComparisonResult(Person *person1, Person *person2) {
    return [person1.birthDate compare:person2.birthDate];
}];

Для сортировки по убыванию:

objectivec
NSArray *sortedArray = [personArray sortedArrayUsingComparator:^NSComparisonResult(Person *person1, Person *person2) {
    return [person2.birthDate compare:person1.birthDate]; // Обратное сравнение
}];

Этот подход похож на Collections.sort в Java с кастомным Comparator и предоставляет:

  • Гибкость без изменения классов
  • Легкое переключение между порядком возрастания и убывания
  • Возможность выполнения сложных сравнений внутри блока

Рекомендации по производительности

При выборе между этими подходами учитывайте производительность:

  1. Прямые вызовы методов (метод compare:) обычно являются самыми быстрыми, так как они избегают накладных расходов на передачу сообщений и KVC.

  2. Компараторы на основе блоков немного медленнее, чем прямые вызовы методов, но более гибкие.

  3. NSSortDescriptor является самым медленным из-за накладных расходов KVC, но предлагает наибольшую гибкость для сложных сценариев сортировки.

Как указано в документации Riptutorial, “Методы -compare: и на основе блоков будут значительно быстрее, в общем, чем использование NSSortDescriptor, так как последний relies на KVC.”

Для приложений с критически важной производительностью и большими массивами рекомендуется метод compare:. Для большинства приложений подход на основе блоков обеспечивает хороший баланс между производительностью и гибкостью.

Полный пример реализации

Вот полный пример, демонстрирующий все три подхода:

Person.h:

objectivec
#import <Foundation/Foundation.h>

@interface Person : NSObject
@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) NSDate *birthDate;
@end

Person.m:

objectivec
#import "Person.h"

@implementation Person

// Метод 1: Реализация метода compare:
- (NSComparisonResult)compare:(Person *)otherObject {
    return [self.birthDate compare:otherObject.birthDate];
}

@end

Пример сортировки:

objectivec
// Создание тестовых данных
NSMutableArray *people = [NSMutableArray array];
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
formatter.dateFormat = @"yyyy-MM-dd";

[people addObject:[[Person alloc] initWithName:@"Alice" birthDate:[formatter dateFromString:@"1990-05-15"]]];
[people addObject:[[Person alloc] initWithName:@"Bob" birthDate:[formatter dateFromString:@"1985-12-20"]]];
[people addObject:[[Person alloc] initWithName:@"Charlie" birthDate:[formatter dateFromString:@"1992-01-10"]]];

// Метод 1: Использование метода compare:
NSArray *sortedByCompare = [people sortedArrayUsingSelector:@selector(compare:)];
NSLog(@"Отсортировано методом compare: %@", sortedByCompare);

// Метод 2: Использование NSSortDescriptor
NSSortDescriptor *dateDescriptor = [[NSSortDescriptor alloc] initWithKey:@"birthDate" ascending:YES];
NSArray *sortedByDescriptor = [people sortedArrayUsingDescriptors:@[dateDescriptor]];
NSLog(@"Отсортировано через NSSortDescriptor: %@", sortedByDescriptor);

// Метод 3: Использование компаратора на основе блока
NSArray *sortedByComparator = [people sortedArrayUsingComparator:^NSComparisonResult(Person *person1, Person *person2) {
    return [person1.birthDate compare:person2.birthDate];
}];
NSLog(@"Отсортировано через компаратор блока: %@", sortedByComparator);

Источники

  1. How do I sort an NSMutableArray with custom objects in it? - Stack Overflow
  2. Sorting array with custom objects - Riptutorial
  3. Sorting an NSMutableArray with Custom Objects in Objective-C - Repeato
  4. Sort array of objects by their NSDate property - Stack Overflow
  5. Creating and Using Sort Descriptors - Apple Developer Documentation

Заключение

Сортировка NSMutableArray с кастомными объектами в Objective-C по свойствам NSDate может быть выполнена тремя основными способами:

  1. Реализуйте метод compare: в вашем кастомном классе для чистой, инкапсулированной сортировки, наиболее похожей на интерфейс Comparable в Java
  2. Используйте NSSortDescriptor для гибкой сортировки с Key-Value Coding, особенно полезно при множественных критериях сортировки
  3. Используйте sortedArrayUsingComparator: с блоками для максимальной гибкости без изменения классов

Для большинства приложений подход на основе блоков обеспечивает лучший баланс между производительностью и гибкостью. Выбирайте метод compare: для сценариев с критически важной производительностью и NSSortDescriptor, когда вам нужны возможности сложной сортировки или динамического изменения порядка сортировки.

Каждый подход имеет свое место в вашем наборе инструментов Objective-C, и понимание всех трех сделает вас более универсальным разработчиком iOS.

Авторы
Проверено модерацией
Модерация