Словари: наборы ключей и значений

Словари управляют парами ключей и значений. Пару ключ/значение в словаре вызывают записью. Каждая запись состоит из одного объекта, представляющего ключ и второй объект, который является что значение ключа. В словаре ключи уникальны — т.е. никакие два ключа в единственном словаре не равны (как определено isEqual:). Ключ может быть любым объектом, принимающим NSCopying протокол и реализации hash и isEqual: методы. Рисунок 1 показывает словарь, содержащий информацию о гипотетическом лице. Как показано значение, содержавшееся в словаре, может быть любым объектом, даже другой набор.

  Словарь рисунка 1 В качестве примера

Основные принципы словаря

NSDictionary объект управляет неизменным словарем — т.е. после создания словаря Вы не можете добавить, удалить или заменить ключи и значения. Можно, однако, изменить сами отдельные значения (если они поддерживают модификацию), но не должны быть изменены ключи. Переменчивость набора не влияет на переменчивость объектов в наборе. Если словарь редко изменяется или изменяет оптовую торговлю, необходимо использовать неизменный словарь.

NSMutableDictionary объект управляет непостоянным словарем, позволяющим дополнение и удаление записей в любое время, автоматически выделяя память по мере необходимости. Необходимо использовать непостоянный словарь, если словарь изменяется инкрементно или является очень большим — поскольку большое количество занимает больше времени для инициализации.

Можно легко создать экземпляр одного типа словаря от другого использования инициализатора initWithDictionary: или конструктор удобства dictionaryWithDictionary:.

В целом Вы инстанцируете словаря путем отправки одного из dictionary...сообщения любому NSDictionary или NSMutableDictionary класс. dictionary... сообщения возвращают словарь, содержащий ключи, и оценивает Вас передача в как параметры. Объекты добавили, поскольку значения к словарю не копируются (если Вы не передаете YES как параметр initWithDictionary:copyItems:). Скорее сильная ссылка к объекту добавляется к словарю. Для получения информации о том, как словарь обрабатывает ключевые объекты, посмотрите Используя Пользовательские Ключи. Для получения дополнительной информации о копировании и управлении памятью, посмотрите Копирование Наборов.

Внутренне, словарь использует хэш-таблицу, чтобы организовать ее хранение и обеспечить быстрый доступ к значению, данному соответствующий ключ. Однако методы, определенные для словарей, изолируют Вас от сложностей работы с хэш-таблицами, хеш-функциями или хешированным значением ключей. Методы берут ключи непосредственно, не в их хешированной форме.

Используя непостоянные словари

При удалении записи из непостоянного словаря помните, что сильная ссылка словаря к ключу и оценивает объекты, составляющие запись, отбрасываются. Если нет никаких дальнейших сильных ссылок к объектам, они освобождены.

Добавление объектов к непостоянному словарю является относительно прямым. Добавить единственную пару ключ/значение или заменить объект для определенного ключа, использование setObject:forKey: метод экземпляра, как показано в Перечислении 1.

Перечисление 1  , Добавляющее объекты к словарю

NSString *last = @"lastName";
NSString *first = @"firstName";
 
NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithObjectsAndKeys:
        @"Jo", first, @"Smith", last, nil];
NSString *middle = @"middleInitial";
 
[dict setObject:@"M" forKey:middle];

Можно также добавить записи из другого словаря с помощью addEntriesFromDictionary: метод экземпляра. Если оба словаря содержат тот же ключ, предыдущий объект значения получателя для того ключа выпущен, и новый объект занимает свое место. Например, после того, как код в Перечислении 2 выполняется, dict имел бы значение «Джонса» для ключа «lastName».

Перечисление 2  , Добавляющее записи из другого словаря

NSString *last = @"lastName";
NSString *first = @"firstName";
NSString *suffix = @"suffix";
NSString *title = @"title";
 
NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithObjectsAndKeys:
    @"Jo", first, @"Smith", last, nil];
 
NSDictionary *newDict = [NSDictionary dictionaryWithObjectsAndKeys:
    @"Jones", last, @"Hon.", title, @"J.D.", suffix, nil];
 
[dict addEntriesFromDictionary: newDict];

Сортировка словаря

NSDictionary обеспечивает метод keysSortedByValueUsingSelector:, который возвращает массив ключей словаря в порядке, они были бы в том, если бы словарь был сортирован его значениями, как проиллюстрировано в Перечислении 3.

Перечисление 3  , Сортирующее ключи словаря значением

NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:
    [NSNumber numberWithInt:63], @"Mathematics",
    [NSNumber numberWithInt:72], @"English",
    [NSNumber numberWithInt:55], @"History",
    [NSNumber numberWithInt:49], @"Geography",
    nil];
 
NSArray *sortedKeysArray =
    [dict keysSortedByValueUsingSelector:@selector(compare:)];
// sortedKeysArray contains: Geography, History, Mathematics, English

Можно также использовать блоки для простой сортировки ключей словаря на основе их соответствующих значений. keysSortedByValueUsingComparator: метод NSDictionary позволяет Вам использовать блок для сравнения ключей, которые будут сортированы в новый массив. Перечисление 4 иллюстрирует сортировку с блоком.

  Блоки перечисления 4 упрощают пользовательскую сортировку словарей

NSArray *blockSortedKeys = [dict keysSortedByValueUsingComparator: ^(id obj1, id obj2) {
 
     if ([obj1 integerValue] > [obj2 integerValue]) {
          return (NSComparisonResult)NSOrderedDescending;
     }
 
     if ([obj1 integerValue] < [obj2 integerValue]) {
          return (NSComparisonResult)NSOrderedAscending;
     }
     return (NSComparisonResult)NSOrderedSame;
}];

Используя пользовательские ключи

В большинстве случаев, Предоставленный какао объекты такой как NSString объекты должны быть достаточными для использования в качестве ключей. В некоторых случаях, однако, может быть необходимо использовать пользовательские объекты в качестве ключей в словаре. При использовании пользовательских объектов как ключи существуют некоторые важные моменты для учета.

Ключи должны соответствовать NSCopying протокол. Методы, добавляющие записи в словари — не добавляют ли как часть инициализации (для всех словарей) или во время модификации (для непостоянных словарей) — каждый ключевой объект к словарю непосредственно. Вместо этого они копируют каждый ключевой аргумент и добавляют копию к словарю. Будучи скопированным в словарь, копии находившиеся в собственности словаря ключей не должны быть изменены.

Ключи должны реализовать hash и isEqual: методы, потому что словарь использует хэш-таблицу, чтобы организовать ее хранение и быстро получить доступ к содержащим в нем объектам. Кроме того, производительность в словаре в большой степени зависит от используемой хеш-функции. С плохой хеш-функцией уменьшение в производительности может быть серьезным. Для получения дополнительной информации о hash и isEqual: методы видят NSObject.

Используя таблицы карты

NSMapTable класс сконфигурирован по умолчанию для содержания объектов во многом как NSMutableDictionary. Это также позволяет дополнительные возможности хранения, которые можно адаптировать для конкретных случаев, такой как тогда, когда Вам нужны усовершенствованные опции управления памятью, или когда Вы хотите содержать определенный тип указателя. Например, таблица карты на рисунке 2 сконфигурирована для содержания слабых ссылок на ее объекты значения. Можно также указать, хотите ли Вы скопировать объекты, ввел в массив.

  Владение объекта таблицы Карты рисунка 2

Можно использовать NSMapTable возразите, когда Вы хотите набор пар ключ/значение, использующий слабые ссылки. Например, предположите, что у Вас есть глобальная таблица карты, содержащая некоторые объекты. Поскольку глобальные объекты никогда не собираются, ни одно из его содержания не может быть освобождено, если они не сохранены слабо. Таблицам карты, сконфигурированным для содержания объектов слабо, не принадлежит их содержание. Если нет никаких сильных ссылок к объектам в такой таблице карты, те объекты освобождены. Например, таблица карты на рисунке 2 содержит слабые ссылки на свое содержание. Возразите D, и Объект E будет освобожден, но остается остальная часть объектов.

Для составления таблицы карты создайте или инициализируйте его использование mapTableWithKeyOptions:valueOptions: или initWithKeyOptions:valueOptions:capacity: и надлежащие опции функций указателя. Также можно инициализировать его использование initWithKeyPointerFunctions:valuePointerFunctions:capacity: и надлежащие экземпляры NSPointerFunctions. Для получения дополнительной информации об опциях функций указателя посмотрите Опции Функции Указателя.

NSMapTable класс также определяет много конструкторов удобства для того, чтобы составить таблицу карты с сильными или слабыми ссылками на ее содержание. Например, mapTableWithStrongToWeakObjects составляет таблицу карты, содержащую сильные ссылки к ее ключам и слабые ссылки на ее значения. Если Вы храните объекты, эти конструкторы удобства должны только использоваться.

Для конфигурирования таблицы карты для использования произвольных указателей инициализируйте его с обоими NSPointerFunctionsOpaqueMemory и NSPointerFunctionsOpaquePersonality опции значения. Ключ и опции значения не должны быть тем же. При использовании таблицы карты для содержания произвольных указателей C функционируют API для void * указатели должны использоваться. Для получения дополнительной информации посмотрите Управляющие Таблицы Карты. Например, можно добавить указатель на int значение с помощью подхода, показанного в Перечислении 5. Обратите внимание на то, что табличное использование карты NSString объекты как ключи, и это вводит, копируются в таблицу карты.

  Таблица Listing 5 Map сконфигурирована для необъектных указателей

NSPointerFunctionsOptions keyOptions=NSPointerFunctionsStrongMemory |
     NSPointerFunctionsObjectPersonality | NSPointerFunctionsCopyIn;
NSPointerFunctionsOptions valueOptions=NSPointerFunctionsOpaqueMemory |
     NSPointerFunctionsOpaquePersonality;
 
NSMapTable *mapTable = [NSMapTable mapTableWithKeyOptions:keyOptions
        valueOptions:valueOptions];
 
NSString *key1 = @"Key1";
NSMapInsert(mapTable, key1, someIntPtr);

Можно тогда получить доступ к тому целому числу при помощи NSMapGet функция.

NSLog(@" Key1 contains: %i", *(int *) NSMapGet(mapTable, @"Key1"));

Когда сконфигурировано для использования произвольных указателей таблице карты связали риски с использованием указателей. Например, если указатели относятся к стековым данным, создаваемым в функции, те указатели не допустимы за пределами функции, даже если таблица карты. Попытка получить доступ к ним приведет к неопределенному поведению.