NSPnetworkingicate versus NSString: ¿Cuál es mejor / más rápido para encontrar superstrings?

Tengo una gran cantidad de cadenas que estoy buscando para ver si existe una subcadena determinada. Parece que hay dos maneras razonables de hacer esto.

Opción 1: utilice el método rangeOfSubstring y pruebe si existe la .location :

 NSRange range = [string rangeOfSubstring:substring]; return (range.location != NSNotFound); 

Opción 2. Utilice la syntax NSPnetworkingicate CONTAINS :

 NSPnetworkingicate *regex = [NSPnetworkingicate pnetworkingicateWithFormat:@"SELF CONTAINS %@", substring]; return ([regex evaluateWithObject:string] == YES) 

¿Qué método es mejor, o hay una buena opción 3 que me falta por completo? No, no estoy seguro de exactamente a qué me refiero con "mejor", pero posiblemente me refiero a más rápido cuando se itera en muchas, muchas string .

Debe comparar y medir cualquier solución que use NSPnetworkingicate porque, en mi experiencia, NSPnetworkingicate puede ser muy lenta.

Para simplificar, iría con un simple for(NSString *string in stringsArray) { } tipo de bucle. El cuerpo del bucle contendría una simple verificación de rangeOfSubstring . Es posible que pueda mejorar el performance de esto en unos pocos por ciento usando CFStringFind() , pero solo verá un beneficio si está buscando a través de muchas cadenas. La ventaja de usar CFStringFind() es que puede evitar la sobrecarga de envío de posts (muy pequeño) Objective-C. Una vez más, usualmente es solo una victoria cambiar a eso cuando estás buscando "muchas" cadenas (para algunos siempre cambiando el valor de "mucho"), y siempre deberías compararlas para estar seguro. Prefiero el rangeOfString: más simple de Objective-C rangeOfString: path si puedes.

Un enfoque mucho más complicado es usar la function ^ Blocks con la opción NSEnumerationConcurrent . NSEnumerationConcurrent es solo una sugerencia de que desea que la enumeración ocurra simultáneamente si es posible, y una implementación puede ignorar esta sugerencia si no puede admitir la enumeración simultánea. Sin embargo, es probable que su NSArray estándar vaya a implementar la enumeración simultánea. En la práctica, esto tiene el efecto de dividir todos los objects en NSArray y dividirlos en las CPU disponibles. Debe tener cuidado con la forma de mutar el estado y los objects a los que accede ^ Bloquear a través de varios subprocesss. Aquí hay una forma potencial de hacerlo:

 // 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. 

Utiliza un ligero OSSpinLock para asegurarse de que solo un hilo tenga acceso y actualice matchesArray a la vez. Puede usar la misma sugerencia CFStringFind() de aquí también.

Además, debe tener en count que rangeOfString: no coincidirá por sí mismo en "límites de palabra". En el ejemplo anterior, usé la palabra this , que coincidiría con la cadena. A paleolithist walked in to the bar... aunque no contiene la palabra " this .

La solución más simple a esta pequeña arruga es utilizar una expresión regular de la UCI y aprovechar su funcionalidad de "mejora de palabras". Para hacer esto, tiene algunas opciones:

  • NSRegularExpression , actualmente solo está disponible en> 4.2 o> 4.3 iOS (olvidé cuál).
  • RegexKit Lite , a través de RegexKitLite-4.0.tar.bz2
  • NSPnetworkingicate , a través de SELF MATCHES '(?w)\b...\b' . La ventaja de esto es que no requiere nada adicional (es decir, RegexKit Lite ) y está disponible en todas las versiones (?) De Mac OS X y iOS> 3.0.

El siguiente código muestra cómo usar la funcionalidad mejorada de NSPnetworkingicate palabras en las expresiones regulares de la UCI a través de NSPnetworkingicate :

 NSString *searchForString = @"this"; NSString *regexString = [NSString stringWithFormat:@".*(?w:\\b\\Q%@\\E\\b).*", searchForString]; NSPnetworkingicate *wordBoundaryRegexPnetworkingicate = [NSPnetworkingicate pnetworkingicateWithFormat:@"SELF MATCHES %@", regexString]; NSArray *matchesArray = [stringsToSearchArray filtenetworkingArrayUsingPnetworkingicate:wordBoundaryRegexPnetworkingicate]; 

Puede hacer que la búsqueda sea insensible reemplazando (?w: en regexString con (?wi:

La expresión regular, si está interesado, básicamente dice

  • .*(?w:...).* dice "emparejar cualquier cosa hasta y después de la parte (?w:...) " (es decir, solo nos interesa la parte (?w:...) )
  • (?w:...) dice "Activar la function de búsqueda / ruptura de palabras mejorada de la ICU dentro del paréntesis".
  • \\b...\\b (que en realidad es solo una barra invertida, cualquier barra invertida tiene que ser barra invertida cuando está dentro de una cadena @"" dice "Coincidir con un límite de palabras".
  • \\Q...\\E dice "Trata el text que comienza inmediatamente después de \Q y hasta \E como text literal (piensa" Cita "y" Fin ")". En otras palabras, cualquier carácter en el "text literal citado" no tiene su expresión especial de expresión regular.

El motivo de \Q...\E es que probablemente desee hacer coincidir los caracteres literales en searchForString . Sin esto, searchForString trataría como parte de la expresión regular. Como ejemplo, si searchForString fue this? , entonces sin \Q...\E no coincide con la cadena literal this? , pero this o this , que probablemente no sea lo que quieres. 🙂

Caso (n): Si tiene un set de cadenas para probar una cadena secundaria, será mejor usar NSPnetworkingicate .

 NSPnetworkingicate *regex = [NSPnetworkingicate pnetworkingicateWithFormat:@"SELF CONTAINS %@", substring]; NSArray *resultArray = [originalArrayOfStrings filtenetworkingArrayUsingPnetworkingicate:regex]; 

Esto devolverá la matriz de cadenas que contienen la subcadena.

Si utiliza NSRange , en este caso, debe recorrer manualmente todos los objects de cadena de la matriz y, obviamente, será más lento que NSPnetworkingicate .