NSPredicate versus NSSsortingng: Quel est le meilleur / le plus rapide pour find des supercordes?

J'ai un grand nombre de strings que je cherche à voir si une sous-string existe. Il semble qu'il y ait deux façons raisonnables de le faire.

Option 1: utilisez la méthode NSSsortingng rangeOfSubssortingng et testez si .location existe:

 NSRange range = [ssortingng rangeOfSubssortingng:subssortingng]; return (range.location != NSNotFound); 

Option 2. Utilisez la syntaxe NSPredicate CONTAINS :

 NSPredicate *regex = [NSPredicate predicateWithFormat:@"SELF CONTAINS %@", subssortingng]; return ([regex evaluateWithObject:ssortingng] == YES) 

Quelle méthode est la meilleure, ou y a-t-il une bonne option 3 qui me manque complètement? Non, je ne sais pas exactement ce que je veux dire par "mieux", mais je veux dire peut-être plus rapide quand il s'agit de beaucoup de ssortingng .

    Vous devriez comparer et chronométrer toute solution utilisant NSPredicate car, selon mon expérience, NSPredicate peut être très lent.

    Pour simplifier, j'irais avec un simple type for(NSSsortingng *ssortingng in ssortingngsArray) { } type de boucle. Le corps de la boucle contiendrait une rangeOfSubssortingng simple de rangeOfSubssortingng . Vous pouvez peut-être améliorer les performances de quelques pourcents en utilisant CFSsortingngFind() , mais vous ne verrez un avantage que si vous searchz beaucoup de strings. L'avantage d'utiliser CFSsortingngFind() est que vous pouvez éviter la (très petite) surcharge d'envoi de messages Objective-C. Encore une fois, c'est généralement une victoire de passer à cela lorsque vous searchz «beaucoup» de strings (pour certaines valeurs toujours changeantes de «beaucoup»), et vous devriez toujours faire un benchmark pour être sûr. Préférez le plus simple Objective-C rangeOfSsortingng: façon si vous le pouvez.

    Une approche beaucoup plus compliquée consiste à utiliser la fonction ^ Blocs avec l'option NSEnumerationConcurrent . NSEnumerationConcurrent est seulement un indice que vous souhaitez que l'énumération se produise simultanément si possible, et une implémentation est libre d'ignorer cette indication si elle ne peut pas prendre en charge l'énumération simultanée. Cependant, votre NSArray standard va très probablement implémenter une énumération simultanée. En pratique, cela a pour effet de split tous les objects du NSArray et de les répartir entre les CPU disponibles. Vous devez faire attention à la façon de muter l'état et les objects auxquels le bloc ^ accède via plusieurs threads. Voici une façon possible de le faire:

     // Be sure to #include <libkern/OSAtomic.h> __block volatile OSSpinLock spinLock = OS_SPINLOCK_INIT; __block NSMutableArray *matchesArray = [NSMutableArray array]; [ssortingngsToSearchArray enumerateObjectsWithOptions:NSEnumerationConcurrent usingBlock:^(id obj, NSUInteger idx, BOOL *stop) { NSRange matchedRange = [obj rangeOfSsortingng:@"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 ssortingngs that had a match. 

    Cela utilise un OSSpinLock léger pour s'assurer qu'un seul thread a access et met à jour matchesArray à la fois. Vous pouvez également utiliser la même suggestion CFSsortingngFind() ci-dessus.

    En outre, vous devez être conscient que rangeOfSsortingng: ne correspondra pas, en soi, aux "limites de mots". Dans l'exemple ci-dessus, j'ai utilisé le mot this , qui correspondrait à la string A paleolithist walked in to the bar... même s'il ne contient pas le mot this .

    La solution la plus simple à cette petite ride est d'utiliser une expression régulière de l'ICU et de profiter de la fonctionnalité de «rupture de mot améliorée». Pour ce faire, vous avez quelques options:

    • NSRegularExpression , actuellement disponible uniquement sur> 4.2 ou> 4.3 iOS (j'oublie lequel).
    • RegexKit Lite , via RegexKitLite-4.0.tar.bz2
    • NSPredicate , via SELF MATCHES '(?w)\b...\b' . L'avantage à cela est qu'il ne nécessite rien de plus (par exemple, RegexKit Lite ) et est disponible sur toutes les versions (?) De Mac OS X et iOS> 3.0.

    Le code suivant montre comment utiliser la fonctionnalité de NSPredicate mots améliorée dans les expressions régulières ICU via NSPredicate :

     NSSsortingng *searchForSsortingng = @"this"; NSSsortingng *regexSsortingng = [NSSsortingng ssortingngWithFormat:@".*(?w:\\b\\Q%@\\E\\b).*", searchForSsortingng]; NSPredicate *wordBoundaryRegexPredicate = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", regexSsortingng]; NSArray *matchesArray = [ssortingngsToSearchArray filteredArrayUsingPredicate:wordBoundaryRegexPredicate]; 

    Vous pouvez rendre le cas de search insensible en remplaçant le (?w: in regexSsortingng par (?wi: regexSsortingng

    La regex, si vous êtes intéressé, dit essentiellement

    • .*(?w:...).* dit "faire correspondre n'importe quoi jusqu'à et après la partie (?w:...) " (c'est-à-dire que nous ne sums intéressés que par la partie (?w:...) ).
    • (?w:...) dit "Activer la fonction de rupture / search de mots améliorée ICU à l'intérieur de la parenthèse".
    • \\b...\\b (qui n'est en fait qu'une seule barre oblique inverse, tout backslash doit être backslash échappé quand il se trouve dans une string @"" ) dit "Match à une limite de mot".
    • \\Q...\\E dit "Traite le text en commençant immédiatement après \Q et jusqu'à \E tant que text littéral (pensez" Citation "et" Fin ")". En d'autres termes, tous les caractères dans le "text littéral cité" n'ont pas leur signification spéciale regex.

    La raison de \Q...\E est que vous voulez probablement faire correspondre les caractères littéraux dans searchForSsortingng . Sans cela, searchForSsortingng serait traité comme faisant partie de la regex. Par exemple, si searchForSsortingng était this? , alors sans \Q...\E cela ne correspondrait pas à la string littérale this? , mais soit thi ou this , this qui n'est probablement pas ce que vous voulez. 🙂

    Cas (n): Si vous avez un tableau de strings à tester pour une sous-string, il vaudra mieux utiliser NSPredicate .

     NSPredicate *regex = [NSPredicate predicateWithFormat:@"SELF CONTAINS %@", subssortingng]; NSArray *resultArray = [originalArrayOfSsortingngs filteredArrayUsingPredicate:regex]; 

    Cela returnnera un tableau de strings contenant la sous-string.

    Si vous utilisez NSRange , dans ce cas, vous devez parcourir tous les objects string du tableau manuellement, et évidemment, il sera plus lent que NSPredicate .