Thursday, February 21, 2013

Массивы: упорядоченные коллекции Cocoa

http://macbug.ru/cocoa/cdarray#.USY5C1pp5Xd

Массивы: упорядоченные коллекции

Cocoa

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

Рисунок 1 массивы

cocoa array
Информация по производительности:
  • Доступ к элементу массива занимает постоянное время.
  • Присоединение и удаление элементов с обоих концов занимает постоянное время.
  • Замена элемента занимает постоянное время.
  • Вставка элемента в середину массива занимает линейное время.

Основы массивов

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

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

Вы можете легко создать экземпляр одного типа массива из другого, используя инициализатор initWithArray: или удобным конструктором arrayWithArray. Например, если у вас есть экземпляр NSArraymyАггау, вы можете создать изменяемую копию следующим образом:

NSMutableArray *myMutableArray = [NSMutableArray arrayWithArray:myArray];

В общем Вашему экземпляру массива посылается сообщение array... или NSArray или NSMutableArray класса. array... сообщение возвратит массив, содержащий элементы, переданные в качестве аргумента. Когда вы добавляете объект к NSMutableArray объект не копируется (если вы не передали YES в аргументе метода initWithArray:copyItems:). Скорее всего, объект добавляется непосредственно в массив. В среде управления памятью объект получает сообщение retain, когда добавляется к массиву. Когда массив освобождается, каждый элемент получает сообщение release. Для более подробной информации см. раздел "Копирование коллекций".

В NSArray, два основных метода: -count и objectAtIndex: -обеспечивает основу для всех других методов в интерфейсе:

count возвращает количество элементов в массиве.

objectAtIndex: обеспечивает доступ к элементам массива. Отсчет элементов начинается с индекса 0.

Изменяемые массивы

Основные методы в NSMutableArray, перечисленные ниже, служат основой для его способности добавлять, заменять и удалять элементы:
      addObject:      insertObject:atIndex:      removeLastObject      removeObjectAtIndex:      replaceObjectAtIndex:withObject:  

Если Вам не нужен объект для размещения по указанному индексу или удалить из середины коллекции, Вы должны использовать AddObject:,removeLastObject методы, потому что добавлять и удалять в конец массива быстрее, чем в середину.

другие методы в NSMutableArray обеспечивают удобный способ вставки объекта в слот в массиве и удаление объекта на основе его идентичности или позиции в массиве, как показано ниже:

NSMutableArray *array = [NSMutableArray array];  [array addObject:[NSColor blackColor]];  [array insertObject:[NSColor redColor] atIndex:0];  [array insertObject:[NSColor blueColor] atIndex:1];  [array addObject:[NSColor whiteColor]];  [array removeObjectsInRange:(NSMakeRange(1, 2))];  // array теперь содержит redColor и whiteColor  

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

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

Вы можете получить доступ к элементу массива по индексу, используя метод objectAtIndex:

NSString *s=[arrayOfStrings objectAtIndex: 2]; // доступ к третьему элементу массива

Методы objectEnumerator и reverseObjectEnumerator предоставляют последовательный доступ к элементам массива, отличаясь только в направлении движения по элементам. Кроме того, методы makeObjectsPerformSelector: и makeObjectsPerformSelector:withObject: позволяет отправлять сообщения во все объекты в массиве. В большинстве случаев, следует использовать быстрое перечисление, поскольку это быстрее и гибче, чем при использовании NSEnumerator или метода makeObjectsPerformSelector:. Более подробную информацию о перечислении смотрите в разделе "Перечисление. Обход элементов коллекции".

Вы можете извлечь подмножество массива (subarrayWithRange:) или объединить элементы массива объекта NSString в одну строку (componentsJoinedByString:). Кроме того, вы можете сравнить два массива использованием методов isEqualToArray: иfirstObjectCommonWithArray:. Наконец, вы можете создать новый массив, который будет содержать объекты из существующего массива и один или несколько дополнительных объектов arrayByAddingObject: или arrayByAddingObjectsFromArray:.

Существуют два основных способа, которые можно использовать, чтобы определить, присутствует ли объект в массиве, indexOfObject: иindexOfObjectIdenticalTo:. Есть также два варианта, indexOfObject:inRange: и indexOfObjectIdenticalTo:inRange:, которые вы можете использовать для поиска в диапазоне, в пределах массива. Методы indexOfObject: для проверки на равенство, отправляют элементам в массиве сообщенияisEqual:; методы indexOfObjectIdenticalTo: испытывают на равенство с помощью указателя сравнения. Разница показана в листинге ниже:

NSString *yes0 = @"yes";  NSString *yes1 = @"YES";  NSString *yes2 = [NSString stringWithFormat:@"%@", yes1];    NSArray *yesArray = [NSArray arrayWithObjects: yes0, yes1, yes2, nil];    NSUInteger index;    index = [yesArray indexOfObject:yes2];  // index is 1 (эквивалентен)    index = [yesArray indexOfObjectIdenticalTo:yes2];  // index is 2 (равен)  

Сортировка массивов

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

Рисунок 2 Сортировка массивов

Cocoa NSArray sorting

Сортировка с дескрипторами сортировки

Дескрипторы сортировки (экземпляры NSSortDescriptor) обеспечивают удобный и абстрактный способ описания порядка сортировки. Дескриптор сортировки предоставляет несколько полезных функций. Вы можете легко выполнять большинство операций сортировки с минимальным пользовательским кодом. Вы также можете использовать дескрипторы сортировки в сочетании с Cocoa bindings для сортировки содержимого, например, таблицы. Вы также можете использовать их с Core Data для сортировки результатов запроса выборки.

Если вы используете методы sortedArrayUsingDescriptors: или sortUsingDescriptors:, дескрипторы сортировки обеспечивают простой способ сортировки коллекций объектов, используя ряд их свойств. Учитывая множество словарей (пользовательские объекты работают таким же образом), вы можете отсортировать его содержимое по фамилии, имени. Следующий листинг показывает, как создать этот массив, а затем отсортировать с дескрипторами. (На рисунке 2 показан пример этого примера.)

//Сначала создадим массив из словарей  NSString *LAST = @"lastName";  NSString *FIRST = @"firstName";    NSMutableArray *array = [NSMutableArray array];  NSArray *sortedArray;    NSDictionary *dict;  dict = [NSDictionary dictionaryWithObjectsAndKeys:                     @"Jo", FIRST, @"Smith", LAST, nil];  [array addObject:dict];    dict = [NSDictionary dictionaryWithObjectsAndKeys:                     @"Joe", FIRST, @"Smith", LAST, nil];  [array addObject:dict];    dict = [NSDictionary dictionaryWithObjectsAndKeys:                     @"Joe", FIRST, @"Smythe", LAST, nil];  [array addObject:dict];    dict = [NSDictionary dictionaryWithObjectsAndKeys:                     @"Joanne", FIRST, @"Smith", LAST, nil];  [array addObject:dict];    dict = [NSDictionary dictionaryWithObjectsAndKeys:                     @"Robert", FIRST, @"Jones", LAST, nil];  [array addObject:dict];    //Далее сортируем содержимое массива по last name затем first name    // Результаты могут быть показаны пользователю  // Обратите внимание на использование localizedCaseInsensitiveCompare: selector    NSSortDescriptor *lastDescriptor =      [[[NSSortDescriptor alloc] initWithKey:LAST                ascending:YES                selector:@selector(localizedCaseInsensitiveCompare:)] autorelease];  NSSortDescriptor *firstDescriptor =      [[[NSSortDescriptor alloc] initWithKey:FIRST                ascending:YES                selector:@selector(localizedCaseInsensitiveCompare:)] autorelease];    NSArray *descriptors = [NSArray arrayWithObjects:lastDescriptor,                                     firstDescriptor, nil];  sortedArray = [array sortedArrayUsingDescriptors:descriptors];  

Направление и первый параметр сортировки легко изменить концептуально и программно, как показано ниже:

NSSortDescriptor *lastDescriptor =          [[[NSSortDescriptor alloc] initWithKey:LAST               ascending:NO               selector:@selector(localizedCaseInsensitiveCompare:)] autorelease];    NSSortDescriptor *firstDescriptor =      [[[NSSortDescriptor alloc] initWithKey:FIRST               ascending:NO               selector:@selector(localizedCaseInsensitiveCompare:)] autorelease];    NSArray *descriptors = [NSArray arrayWithObjects:firstDescriptor,               lastDescriptor, nil];  sortedArray = [array sortedArrayUsingDescriptors:descriptors];  

В частности, это несложно для создания подобных дескрипторов от пользовательского ввода.

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

NSInteger lastNameFirstNameSort(id person1, id person2, void *reverse)  {      NSString *name1 = [person1 valueForKey:LAST];      NSString *name2 = [person2 valueForKey:LAST];        NSComparisonResult comparison = [name1 localizedCaseInsensitiveCompare:name2];      if (comparison == NSOrderedSame) {          name1 = [person1 valueForKey:FIRST];          name2 = [person2 valueForKey:FIRST];          comparison = [name1 localizedCaseInsensitiveCompare:name2];      }        if (*(BOOL *)reverse == YES) {          return 0 - comparison;      }      return comparison;  }    BOOL reverseSort = YES;  sortedArray = [array sortedArrayUsingFunction:lastNameFirstNameSort      context:&reverseSort];  

Сортировка с блоками

Вы можете использовать блоки, чтобы отсортировать массив на основе пользовательских критериев. Метод NSArray sortedArrayUsingComparator:сортирует массив в новый массив, используя блок для сравнения объектов. Метод sortUsingComparator: класса NSMutableArray сортирует массив на месте, используя блок для сравнения объектов. Листинг ниже показывает, сортировку с блоком.

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

Сортировка с помощью функций и селекторов

Следующий листинг иллюстрирует использование методов sortedArrayUsingSelector:sortedArrayUsingFunction:context:, иsortedArrayUsingFunction:context:hint:. Самым сложным из этих методов является sortedArrayUsingFunction:context:hint:. Он наиболее эффективен, когда у вас есть большой массив (N записей), которые вам надо отсортировать раз и затем лишь слегка изменить (P добавлений и удалений, где P гораздо меньше, чем N). Вы можете использовать работу, которую вы сделали в оригинальнй сортировке, и сделать своего рода слияние между N "старых" предметов и Р "новых" предметов. Чтобы получить соответствующую подсказку, вы используете sortedArrayHint когда исходный массив был отсортирован, и держите его, пока вам это нужно (если вы хотите, отсортировать массив после того, как он был изменен).

NSInteger alphabeticSort(id string1, id string2, void *reverse)  {      if (*(BOOL *)reverse == YES) {          return [string2 localizedCaseInsensitiveCompare:string1];      }      return [string1 localizedCaseInsensitiveCompare:string2];  }    NSMutableArray *anArray =      [NSMutableArray arrayWithObjects:@"aa", @"ab",        @"ac", @"ad", @"ae", @"af", @"ag",        @"ah", @"ai", @"aj", @"ak", @"al", @"am", @"an",        @"ao", @"ap", @"aq", @"ar", @"as", @"at",        @"au", @"av", @"aw", @"ax", @"ay", @"az", @"ba",        @"bb", @"bc", @"bd", @"bf", @"bg", @"bh",        @"bi", @"bj", @"bk", @"bl", @"bm", @"bn", @"bo",        @"bp", @"bq", @"br", @"bs", @"bt", @"bu",        @"bv", @"bw", @"bx", @"by", @"bz", @"ca", @"cb",        @"cc", @"cd", @"ce", @"cf", @"cg", @"ch",        @"ci", @"cj", @"ck", @"cl", @"cm", @"cn", @"co",        @"cp", @"cq", @"cr", @"cs", @"ct", @"cu",        @"cv", @"cw", @"cx", @"cy", @"cz", nil];  // внимание: anArray отсортирован  NSData *sortedArrayHint = [anArray sortedArrayHint];    [anArray insertObject:@"be" atIndex:5];    NSArray *sortedArray;    // сортировка используя селектор  sortedArray =       [anArray sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)];    // сортировка используя функцию  BOOL reverseSort = NO;  sortedArray =       [anArray sortedArrayUsingFunction:alphabeticSort context:&reverseSort];    // сортировка с подсказкой  sortedArray =       [anArray sortedArrayUsingFunction:alphabeticSort                                    context:&reverseSort                                       hint:sortedArrayHint];  

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

Фильтрация массивов

Классы NSArray и NSMutableArray предоставляют методы для фильтрации содержимого массива. NSArray обеспечиваетfilteredArrayUsingPredicate:, который возвращает новый массив, содержащий объекты в приемнике, который соответствует указанному предикату.NSMutableArray добавляет filterUsingPredicate:, который оценивает содержимое получателя по отношению у указанному предикату и оставляет только те объекты, которые соответствуют. Эти методы показаны ниже. Более подробную информацию о предикатах см. в "Руководстве по программированию предикатов".

NSMutableArray *array =      [NSMutableArray arrayWithObjects:@"Bill", @"Ben", @"Chris", @"Melissa", nil];    NSPredicate *bPredicate =      [NSPredicate predicateWithFormat:@"SELF beginswith[c] 'b'"];  NSArray *beginWithB =      [array filteredArrayUsingPredicate:bPredicate];  // beginWithB содержит { @"Bill", @"Ben" }.    NSPredicate *sPredicate =      [NSPredicate predicateWithFormat:@"SELF contains[c] 's'"];  [array filterUsingPredicate:sPredicate];  // array теперь содержит { @"Chris", @"Melissa" }  

Вы можете также фильтровать массив, используя объект NSIndexSetNSArray обеспечивает objectsAtIndexes:, который возвращает новый массив, содержащий объекты на индексах в соответствующем индексном наборе. NSMutableArray добавляет removeObjectsAtIndexes:, который позволяет фильтровать массив на месте с помощью набора индексов. Для получения дополнительной информации об индексных наборах см. "Индексные наборы: Хранение индексов в массиве".

Массив указателей

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

Класс NSPointerArray по умолчанию настраивается на распоряжение объектами также, как это делает NSMutableArray, кроме того, что он может содержать nilзначения и того, что метод count отражает эти нулевые значения. Он также позволяет дополнительные опции по хранению данных, которые вы можете приспособить для конкретных случаев, например, когда вам нужны расширенные возможности управления памятью, или если вы хотите распоряжаться определенным типом указателя. Например, массив указателей на рисунке 3 настроен на распоряжение слабыми (weak) ссылками на его содержимое. Вы также можете указать, хотите ли вы скопировать объекты, которые введены в массив.

Рисунок 3 Массив Указателей на объекты

Cocoa NSPointerArray

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

Для создания массива указателей, создайте или инициализируйте его с помощью pointerArrayWithOptions: или initWithOptions:, и соответствующими параметрами NSPointerFunctionsOptions. Альтернативно вы можете инициализировать его с помощью initWithPointerFunctions:, и соответствующих экземпляров NSPointerFunctions.

Класс NSPointerArray также определяет ряд удобных конструкторов для создания массива указателей с сильными или слабыми ссылками на его содержимое. Например, pointerArrayWithWeakObjects создает массив указателей, который содержит слабые ссылки на его содержимое. Эти удобства конструкторов должны использоваться только если вы храните объекты.

Для настройки массива указателей использующий произвольные указатели можно инициализировав его с опциями NSPointerFunctionsOpaqueMemory и NSPointerFunctionsOpaquePersonality. Например, вы можете добавить указатель на целочисленное значение, используя подход, показанный в листинге ниже:

NSPointerFunctionOptions options=(NSPointerFunctionsOpaqueMemory |       NSPointerFunctionsOpaquePersonality);     NSPointerArray *ptrArray=[NSPointerArray pointerArrayWithOptions: options];    [ptrArray addPointer: someIntPtr];  

Вы можете получить доступ к целым, как показано ниже.

NSLog(@" Index 0 contains: %i", *(int *) [ptrArray pointerAtIndex: 0] );

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

No comments:

Post a Comment