Словари: коллекции ключ-значение

Cocoa

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

Cocoa NSDictionary

 

Информация по производительности:
    На практике:
  • Доступ к элементу словаря занимает постоянное время.
  • Установка и удаление занимают постоянное время.

Эти показатели действительны при использовании ключей, таких как NSString, которые имеют предопределенную hash-функцию. При использовании пользовательских объектов в качестве ключей, производительность зависит от hash-метода определенного для объектов. При плохой hash-функции, доступ и редактирование выполняется за линейное время.

Основы словаря

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

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

Вы можете легко создать экземпляр одного типа словаря с другим, используя инициализатор initWithDictionary: или удобный конструктор dictionaryWithDictionary:. Например, если у вас есть экземпляр NSDictionary, myDictionary, вы можете создать изменяемую копию следующим образом:

NSMutableDictionary *myMutableDictionary = 
        [NSMutableDictionary dictionaryWithDictionary: myDictionary];

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

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

Использование изменяемых словарей

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

Доступ к объекту после его удаления, опасно

id anObject = [aDictionary objectForKey:theKey];

[aDictionary removeObjectForKey:theKey];
[anObject someMessage]; // возможно падение приложения

Чтобы защититься от этой ситуации, вы можете сохранить объект вызовом retain, прежде чем удалить его

id anObject = [[aDictionary objectForKey:theKey] retain];

[aDictionary removeObjectForKey:theKey];
[anObject someMessage];
//не забудьте отправить anObject сообщение release, когда вы c ним закончите

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

NSString *LAST=@"lastName";
NSString *FIRST=@"firstName";

NSMutableDictionary *dict=[NSMutableDictionary dictionaryWithObjectsAndKeys:
    @"Jo", FIRST, @"Smith", LAST, nil];
NSString *MIDDLE=@"middleInitial";

[dict setObject: @"M" forKey:MIDDLE];

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

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:, который возвращает массив ключей словаря в порядке, как если бы словарь был отсортирован по его значениям

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 содержит: (Geography, History, Mathematics, English)

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

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;
}];

Использование пользовательских ключей

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

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

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

Важно: Поскольку словарь копирует каждый ключ, ключи должны соответствовать NSCopying протоколу. Имейте это в виду, при выборе объектов для использования в качестве ключей. Хотя вы можете использовать любой объект, который принимает ,NSCopying протокол и реализует hash и isEqual: методы, обычно это плохой стиль, использовать крупные объекты, такие как экземпляры NSImage, так как это может потребовать неподъемной производительности.

Использование карт таблиц

IOS Примечание: класс NSMapTable не доступен в IOS.

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

Рисунок 2 Карта Таблица объектов

Cocoa NSMapTable

Вы можете использовать NSMapTable объект, если вы хотите получить коллекцию пар ключ-значение которые используют слабые (weak) ссылки в среде сборки мусора. Например, предположим, у вас есть глобальная карта таблица, которая содержит некоторые объекты. Поскольку глобальные объекты не собираются, ни один из его содержимых объектов не может быть освобожден, если они проводятся слабо. Карта таблиц настроена для хранения объектов слабо не владея их содержимым. Если нет сильных ссылок на объекты в такой таблице карте, эти объекты могут быть собраны. Например, карта таблиц на рисунке 2, имеет слабые ссылки на содержимое. В течение следующего сбора мусора, объекты D и E освобождаются, а остальные объекты остаются. Если это должным образом не учитывать, такая практика может привести к непредсказуемому поведению.

Чтобы создать карту таблицу, создайте или инициализируйте ее с помощью mapTableWithKeyOptions:valueOptions: или initWithKeyOptions:valueOptions:capacity:, и соответствующим указателем функции вариантов. Кроме того, вы можете инициализировать ее с помощью initWithKeyPointerFunctions:valuePointerFunctions:capacity:, и соответствующими экземплярами NSPointerFunctions.

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

Важно только параметры, перечисленные в NSMapTableOptions гарантируют то, что остальная часть API будет работать правильно, в том числе копирование, архивирование и быстрое перечисление. В то время как другие варианты NSPointerFunctions используются для определенных конфигураций, таких, как хранение произвольных указателей, не все комбинации параметров являются действительными. С некоторыми комбинациями, карты таблицы могут работать неправильно или не могут даже правильно инициализироваться.

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

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"));

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

 
 
homeЗаметили ошибкукарта сайта 
   Made on a Mac