Вы должны оценить и определить время любого решения, которое использует NSPredicate
, потому что по моему опыту NSPredicate
может быть очень медленным.
Для простоты я бы использовал простой цикл for(NSString *string in stringsArray) { }
. Тело цикла будет содержать простую проверку rangeOfSubstring
. Возможно, вы сможете улучшить производительность на несколько процентов, используя CFStringFind()
, но вы увидите преимущество только в том случае, если вы будете искать по множеству строк. Преимущество использования CFStringFind()
состоит в том, что вы можете избежать (очень небольших) накладных расходов на отправку сообщений Objective-C. Опять же, это обычно только выигрыш, чтобы переключиться на это, когда вы ищете «много» строк (для некоторого всегда изменяющегося значения «много»), и вы всегда должны тестировать, чтобы быть уверенным. Предпочитайте более простой способ Objective-C rangeOfString:
, если можете.
Гораздо более сложный подход - использовать функцию ^ Blocks с опцией NSEnumerationConcurrent
. NSEnumerationConcurrent
- это только подсказка, что вы хотите, чтобы перечисление происходило одновременно, если это возможно, и реализация может игнорировать эту подсказку, если она не может поддерживать одновременное перечисление. Однако ваш стандарт NSArray
, скорее всего, будет реализовывать параллельное перечисление. На практике это приводит к разделению всех объектов в NSArray
и разделению их по доступным процессорам. Вы должны быть осторожны с тем, как изменить состояние и объекты, к которым имеет доступ блок ^ через несколько потоков. Вот один из возможных способов сделать это:
// Be sure to #include <libkern/OSAtomic.h>
__block volatile OSSpinLock spinLock = OS_SPINLOCK_INIT;
__block NSMutableArray *matchesArray = [NSMutableArray array];
[stringsToSearchArray enumerateObjectsWithOptions:NSEnumerationConcurrent usingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSRange matchedRange = [obj rangeOfString:@"this"];
if(matchedRange.location != NSNotFound) {
OSSpinLockLock((volatile OSSpinLock * volatile)&spinLock);
[matchesArray addObject:obj];
OSSpinLockUnlock((volatile OSSpinLock * volatile)&spinLock);
}
}];
// At this point, matchesArray will contain all the strings that had a match.
Используется облегченный OSSpinLock
, чтобы убедиться, что только один поток имеет доступ и обновляет matchesArray
одновременно. Вы можете использовать то же самое предложение CFStringFind()
сверху здесь.
Кроме того, вы должны знать, что rangeOfString:
само по себе не будет соответствовать "границам слов". В приведенном выше примере я использовал слово this
, которое будет соответствовать строке A paleolithist walked in to the bar...
, даже если оно не содержит слова this
.
Самое простое решение этой маленькой складки - использовать регулярное выражение ICU и воспользоваться его функциональностью «расширенного разбиения по словам». Для этого у вас есть несколько вариантов:
NSRegularExpression
, в настоящее время доступно только на> 4.2 или> 4.3 iOS (я забыл, какой).
- RegexKit Lite , через RegexKitLite-4.0.tar.bz2
NSPredicate
, через SELF MATCHES '(?w)\b...\b'
. Преимущество этого состоит в том, что он не требует ничего лишнего (то есть RegexKit Lite ) и доступен во всех (?) Версиях Mac OS X и iOS> 3.0.
Следующий код показывает, как использовать расширенные функциональные возможности разбиения по словам в регулярных выражениях ICU через NSPredicate
:
NSString *searchForString = @"this";
NSString *regexString = [NSString stringWithFormat:@".*(?w:\\b\\Q%@\\E\\b).*", searchForString];
NSPredicate *wordBoundaryRegexPredicate = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", regexString];
NSArray *matchesArray = [stringsToSearchArray filteredArrayUsingPredicate:wordBoundaryRegexPredicate];
Вы можете сделать поиск нечувствительным к регистру, заменив (?w:
в regexString
на (?wi:
.
Регулярное выражение, если вам интересно, в основном говорит
.*(?w:...).*
говорит "сопоставить что-либо до и после (?w:...)
части" (т.е. мы заинтересованы только в части (?w:...)
).
(?w:...)
говорит: «Включите в скобках расширенную функцию прерывания / поиска слов ICU».
\\b...\\b
(который на самом деле является всего лишь одним обратным слешем, любой обратный слеш должен быть экранирован, когда он находится внутри строки @""
), говорит «Совпадение на границе слова».
\\Q...\\E
говорит: «Обрабатывайте текст, начинающийся сразу после \Q
и до \E
, как буквальный текст (подумайте« Цитата »и« Конец »)». Другими словами, любые символы в «цитируемом буквальном тексте» не имеют своего специального регулярного значения.
Причина \Q...\E
в том, что вы, вероятно, хотите сопоставить буквенные символы в searchForString
. Без этого searchForString
будет рассматриваться как часть регулярного выражения. Например, если searchForString
было this?
, то без \Q...\E
было бы не , совпадающее с литеральной строкой this?
, но либо thi
или this
что, вероятно, не то, что вы хотите. :)