NSString rangeOfString执行速度慢

时间:2015-03-12 06:55:57

标签: ios objective-c nsstring

我有大约10-15个检查,我在检查字符串中是否存在子字符串。其中一项检查的示例如下:

if([request.URL.absoluteString.lowercaseString rangeOfString:@"/group/editgroupmembers?groupid"].location != NSNotFound)  

我发现通过这些检查需要几秒钟。为了确认,我调试了应用程序,发现每次检查需要1秒以上 rangeOfString是用于检查子字符串存在的Apple's推荐方法 但为什么需要那么长时间?

2 个答案:

答案 0 :(得分:3)

Apple String Programming Guide中,您还会看到:

  

如果您只想确定字符串是否包含给定模式,则可以使用谓词:

因此,在您的情况下,您可以使用谓词将您的搜索字符串与您的网址相匹配,从而为您提供更好的效果:

NSString *path = @"/group/editgroupmembers?groupid";
NSPredicate *containsPred = [NSPredicate predicateWithFormat:@"SELF contains[cd] %@", path];

BOOL match = [containsPred evaluateWithObject:request.URL.absoluteString.lowercaseString];

您可以在Apple Predicate Programming Guide中了解有关谓词的更多信息。

修改

我做了一个快速的基准来检查各种选项的性能。

  • 第一个选项是使用rangeOfString:作为OP。
  • 第二个选项是使用NSPredicate
  • 第三个选项将所有字符串放入一个数组中,并使用filteredArrayUsingPredicate:匹配整个数组。

基准测试将100k随机数字字符串放入数组中,并使用不同的选项检查匹配短语“123”。以下是基准代码:

- (void)benchmarkStringContainment {

    // Setup of array to search through
    NSMutableArray *arrayOfRandomStrings = [NSMutableArray array];
    for (int i = 0; i < 100000; i++) {
        NSString *stringToSearch = [NSString stringWithFormat:@"%lli", arc4random() % 100000000000];
        [arrayOfRandomStrings addObject:stringToSearch];
    }

    // String we search for
    NSString *matchPhrase = @"123";


    // Method 1: rangeOfSubstring
    NSUInteger matches1 = 0;
    NSDate *date1 = [NSDate date];

    for (int i = 0; i < 100000; i++) {
        BOOL match = ([[arrayOfRandomStrings[i] lowercaseString] rangeOfString:matchPhrase].location != NSNotFound);
        if (match) matches1++;
    }

    double timePassed1_ms = [date1 timeIntervalSinceNow] * -1000.0;
    NSLog(@"Method 1: rangeOfString. Matches: %lu, time taken in ms: %f", (unsigned long)matches1, timePassed1_ms);


    // Setup of NSPredicate
    NSPredicate *containsPred = [NSPredicate predicateWithFormat:@"SELF contains[cd] %@", matchPhrase];

    // Method 2: NSPredicate
    NSUInteger matches2 = 0;
    NSDate *date2 = [NSDate date];

    for (int i = 0; i < 100000; i++) {
        BOOL match = [containsPred evaluateWithObject:arrayOfRandomStrings[i]];
        if (match) matches2++;
    }

    double timePassed2_ms = [date2 timeIntervalSinceNow] * -1000.0;
    NSLog(@"Method 2: NSPredicate. Matches: %lu, time taken in ms: %f", (unsigned long)matches2, timePassed2_ms);


    // Method 3: NSPredicate over NSArray
    NSUInteger matches3 = 0;
    NSDate *date3 = [NSDate date];

    NSArray *filteredArray = [arrayOfRandomStrings filteredArrayUsingPredicate:containsPred];
    matches3 = [filteredArray count];

    double timePassed3_ms = [date3 timeIntervalSinceNow] * -1000.0;
    NSLog(@"Method 3: NSPredicate over NSArray. Matches: %lu, time taken in ms: %f", (unsigned long)matches3, timePassed3_ms);

}

在iPhone 5上测试(调试)时,样本结果将产生大约200 ms到500 ms的结果。这是一个典型的输出:

2015-03-13 10:49:27.815 so29003576[1413:494702] Method 1: rangeOfString. Matches: 978, time taken in ms: 443.618000
2015-03-13 10:49:28.090 so29003576[1413:494702] Method 2: NSPredicate. Matches: 978, time taken in ms: 272.534013
2015-03-13 10:49:28.309 so29003576[1413:494702] Method 3: NSPredicate over NSArray. Matches: 978, time taken in ms: 217.999041

下一步,我多次重复基准测试(代码未显示),并对不同方法进行平均,以获得更具代表性的结果:

2015-03-13 10:54:01.728 so29003576[1423:495973] Method 1: rangeOfString. Repetitions: 50, average time taken in ms: 449.577812
2015-03-13 10:54:01.731 so29003576[1423:495973] Method 2: NSPredicate. Repetitions: 50, average time taken in ms: 276.818836
2015-03-13 10:54:01.732 so29003576[1423:495973] Method 3: NSPredicate over NSArray. Repetitions: 50, average time taken in ms: 222.459898

虽然这个小基准肯定不是数据独立的,但它表明了两个结果:

  1. 在一串字符串中找到匹配短语的最快测试选项是选项3,使用filteredArrayUsingPredicate:,其速度是选项1 rangeOfString:的两倍。选项2,在迭代不同的字符串时直接使用NSPredicate,执行情况比选项3稍差。
  2. 基准测试在100.000个字符串中搜索匹配短语。正如其中一条评论中的@ gnasher729所示,OP在其代码中必须存在不同的问题,因为即使最慢的方法在10到15个URL上也应该快几个数量级。

答案 1 :(得分:-1)

如果您要检查网址中的子字符串,那么您可以尝试NSURLComponents

NSURLComponents *urlComponents = [NSURLComponents componentsWithString:request.URL.absoluteString];
if ([urlComponents.path rangeOfString:@"/group/editgroupmembers"].location != NSNotFound) {
  for (NSURLQueryItem *item in urlComponents.queryItems) {
    if ([item.name isEqualToString:@"groupid"]) {
      //your code goes here...
       break;
    }
  }
}

试试这个并告诉我这是否改善了性能。感谢。