Используя предикаты

Этот документ описывает в целом, как Вы используете предикаты, и как использование предикатов может влиять на структуру Ваших данных приложения.

Оценка предикатов

Для оценки предиката Вы используете NSPredicate метод evaluateWithObject: и передача в объекте, против которого будет оценен предикат. Метод возвращает булево значение — в следующем примере, результат YES.

NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF IN %@", @[@"Stig", @"Shaffiq", @"Chris"]];
BOOL result = [predicate evaluateWithObject:@"Shaffiq"];

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

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

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

NSMutableArray *array =
    [NSMutableArray arrayWithObjects:@"Nick", @"Ben", @"Adam", @"Melissa", nil];
 
NSPredicate *bPredicate = [NSPredicate predicateWithFormat:@"SELF beginswith[c] 'a'"];
NSArray *beginWithB = [array filteredArrayUsingPredicate:bPredicate];
// beginWithB contains { @"Adam" }.
 
NSPredicate *sPredicate = [NSPredicate predicateWithFormat:@"SELF contains[c] 'e'"];
[array filterUsingPredicate:sPredicate];
// array now contains { @"Nick", @"Ben", @"Melissa" }

При использовании Базовой платформы Данных методы массива обеспечивают действенные средства фильтрации существующего массива объектов без — как выборка делает — требование цикла обработки к персистентному хранилищу данных.

Используя предикаты с Ключевыми Путями

Вспомните, что можно следовать за отношениями в предикате с помощью ключевого пути. Следующий пример иллюстрирует создание предиката для нахождения сотрудников, принадлежащих отделу с именем (но см. также Производительность).

NSString *departmentName = ... ;
NSPredicate *predicate = [NSPredicate predicateWithFormat:
        @"department.name like %@", departmentName];

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

NSPredicate *predicate = [NSPredicate predicateWithFormat:
    @"ANY employees.firstName like 'Matthew'"];

Если Вы хотите найти Отделы, в которых по крайней мере один из сотрудников выплачен больше, чем определенная сумма, Вы используете ANY оператор как показано в следующем примере:

float salary = ... ;
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"ANY employees.salary > %f", salary];

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

Предикат сравнения не соответствует значения нулю кроме нуля (nil) или NSNull нулевое значение (т.е. ($value == nil) возвраты YES если $value nil). Рассмотрите следующий пример.

NSString *firstName = @"Ben";
 
NSArray *array = @[ @{ @"lastName" : "Turner" }];
                    @{ @"firstName" : @"Ben", @"lastName" : @"Ballard",
                @"birthday", [NSDate dateWithString:@"1972-03-24 10:45:32 +0600"] } ];
 
NSPredicate *predicate =
    [NSPredicate predicateWithFormat:@"firstName like %@", firstName];
NSArray *filteredArray = [array filteredArrayUsingPredicate:predicate];
 
NSLog(@"filteredArray: %@", filteredArray);
// Output:
// filteredArray ({birthday = 1972-03-24 10:45:32 +0600; \\
                      firstName = Ben; lastName = Ballard;})

Предикат действительно соответствует словарь, содержащий значение Ben для ключа firstName, но не соответствует словарь без значения для ключа firstName. Следующий фрагмент кода иллюстрирует тот же тезис с помощью даты и большего - чем компаратор.

NSDate *referenceDate = [NSDate dateWithTimeIntervalSince1970:0];
 
predicate = [NSPredicate predicateWithFormat:@"birthday > %@", referenceDate];
filteredArray = [array filteredArrayUsingPredicate:predicate];
 
NSLog(@"filteredArray: %@", filteredArray);
// Output:
// filteredArray: ({birthday = 1972-03-24 10:45:32 +0600; \\
                       firstName = Ben; lastName = Ballard;})

Тестирование на нуль

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

predicate = [NSPredicate predicateWithFormat:@"(firstName == %@) || (firstName = nil)", firstName];
filteredArray = [array filteredArrayUsingPredicate:predicate];
NSLog(@"filteredArray: %@", filteredArray);
 
// Output:
// filteredArray: ( { lastName = Turner; }, { birthday = 1972-03-23 20:45:32 -0800; firstName = Ben; lastName = Ballard; }

Косвенно, тест для нуля, соответствующего нулевое значение, возвращает true. В следующем фрагменте кода, ok установлен в YES для обеих оценок предиката.

predicate = [NSPredicate predicateWithFormat:@"firstName = nil"];
BOOL ok = [predicate evaluateWithObject:[NSDictionary dictionary]];
 
ok = [predicate evaluateWithObject:
    [NSDictionary dictionaryWithObject:[NSNull null] forKey:@"firstName"]];

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

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

Запросы выборки

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

NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Employee"
        inManagedObjectContext:managedObjectContext];
[request setEntity:entity];
 
NSNumber *salaryLimit = <#A number representing the limit#>;
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"salary > %@", salaryLimit];
[request setPredicate:predicate];
NSError *error;
NSArray *array = [managedObjectContext executeFetchRequest:request error:&error];

Объектные контроллеры

При использовании привязки Какао можно указать предикат выборки для объектного контроллера (такого как экземпляр NSObjectController или NSArrayController). Можно ввести предикат непосредственно в редактора предиката текстовое поле в Инспекторе Атрибутов в XCode, или можно установить его программно использование setFetchPredicate:. Когда контроллер выполняет выборку, предикат используется для ограничения результатов, возвращенных. Если Вы используете NSObjectController объект, Вы указываете выборку, однозначно определяющую объект, Вы хотите быть содержанием контроллера — например, если объектом контроллера является Отдел, предикат мог бы быть name like "Engineering".

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

MATCHES оператор использует пакет Регулярных выражений ICU's, как проиллюстрировано в следующем примере:

NSArray *array = @[@"TATACCATGGGCCATCATCATCATCATCATCATCATCATCATCACAG",
                   @"CGGGATCCCTATCAAGGCACCTCTTCG", @"CATGCCATGGATACCAACGAGTCCGAAC",
                   @"CAT", @"CATCATCATGTCT", @"DOG"];
 
// find strings that contain a repetition of at least 3 'CAT' sequences,
// but not followed by a further 'CA'
NSPredicate *catPredicate =
    [NSPredicate predicateWithFormat:@"SELF MATCHES '.*(CAT){3,}(?!CA).*'"];
 
NSArray *filteredArray = [array filteredArrayUsingPredicate:catPredicate];
// filteredArray contains just 'CATCATCATGTCT'

Согласно спецификации ICU, метасимволы регулярного выражения не допустимы в наборе образца. Например, регулярное выражение \d{9}[\dxX] не соответствует допустимые числа ISBN (любое пятизначное число или строка с девятью цифрами и буквой 'X'), так как образец установил ([\dxX]) содержит метасимвол (\d). Вместо этого Вы могли записать OR выражение, как показано в следующем примере кода:

NSArray *isbnTestArray = @[@"123456789X", @"987654321x", @"1234567890", @"12345X", @"1234567890X"];
NSPredicate *isbnPredicate =
    [NSPredicate predicateWithFormat:@"SELF MATCHES '\\\\d{10}|\\\\d{9}[Xx]'"];
 
NSArray *isbnArray = [isbnTestArray filteredArrayUsingPredicate:isbnPredicate];
// isbnArray contains (123456789X, 987654321x, 1234567890)

Соображения производительности

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

NSPredicate *predicate = [NSPredicate predicateWithFormat:
    @"( title matches .*mar[1-10] ) OR ( type = 1 )"];

необходимо записать

NSPredicate *predicate = [NSPredicate predicateWithFormat:
    @"( type = 1 ) OR ( title matches .*mar[1-10] )"];

Во втором примере оценено регулярное выражение, только если первый пункт является ложью.

Используя соединения

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

NSPredicate *predicate = [NSPredicate predicateWithFormat:
        @"department.name like %@", [department name]];

более эффективно записать:

NSPredicate *predicate = [NSPredicate predicateWithFormat:
        @"department == %@", department];

Если предикат содержит больше чем одно выражение, также обычно более эффективно структурировать его для предотвращения соединений. Например, @"firstName beginswith[cd] 'Matt' AND (ANY directreports.paygrade <= 7)" вероятно, будет более эффективным, чем @"(ANY directreports.paygrade <= 7) AND (firstName beginswith[cd] 'Matt')" потому что прежний избегает делать соединение, если не успешно выполняется первый тест.

Структурирование Ваших данных

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

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

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

В OS X можно установить предикат для контроллера массива для фильтрации довольного массив. Можно установить предикат в коде (использование setFilterPredicate:). Можно также связать контроллер массива filterPredicate привязка с методом, возвращающимся NSPredicate объект. Объект, реализующий метод, может быть Владельцем Файла или другим объектом контроллера. При изменении предиката помните, что необходимо сделать так в значении ключа, наблюдая совместимый путь (см., что Значение ключа Наблюдает Руководство по программированию) так, чтобы контроллер массива обновил себя соответственно.

Можно также связать predicate привязка NSSearchField возразите против filterPredicate из контроллера массива. Поле поиска predicate привязка является привязкой мультизначения, описанной в Типах привязки.