2

要解析 URL 的部分查询字符串,我使用此方法:

NSScanner *scanner = [[NSScanner alloc] initWithString:query];
        [scanner setCharactersToBeSkipped:[NSCharacterSet characterSetWithCharactersInString:@"&?"]];

        NSString *parameterString = [NSString new];
        while ([scanner scanUpToString:ampersand intoString:&parameterString]) 
        {
            NSScanner *parameterScanner = [[NSScanner alloc] initWithString:parameterString];

            NSString *name = [NSString new];
            [parameterScanner scanUpToString:isEqual intoString:&name];

            NSString *value = [parameterString substringFromIndex:([name length] + 1)];
            [parameters setObject:value forKey:name];


        }

在这个项目中,我使用的是 ARC,但该方法仍然在这一行泄漏:

[parameterScanner scanUpToString:isEqual intoString:&name];

究竟是什么泄漏,我该如何解决?

4

4 回答 4

6

我怀疑这个名字实际上并没有泄漏,当你认为它是时它根本没有被释放。在 ARC 下,我相信它的scanUpToString:intoString:定义类似于使用NSError. 换句话说,它需要NSString * __autoreleasing *. 因此,传递给它的任何值实际上都是自动释放的,并且在当前自动释放池耗尽之前不会被释放。假设您周围没有任何其他人,那将是运行循环再次运行的时候。如果该内存使用对您来说是个问题,则可以在循环周围放置一个显式的自动释放池,以便对象立即消失:

NSScanner *scanner = [[NSScanner alloc] initWithString:query];
[scanner setCharactersToBeSkipped:[NSCharacterSet characterSetWithCharactersInString:@"&?"]];

@autoreleasepool
{
    NSString *parameterString = [NSString new];
    while ([scanner scanUpToString:ampersand intoString:&parameterString]) 
    {
        NSScanner *parameterScanner = [[NSScanner alloc] initWithString:parameterString];

        NSString *name = [NSString new];
        [parameterScanner scanUpToString:isEqual intoString:&name];

        NSString *value = [parameterString substringFromIndex:([name length] + 1)];
        [parameters setObject:value forKey:name];


    }
}

但这可能是不必要的,并且运行循环无论如何都会清除对象。

也就是说,仍然存在一个小问题,这意味着编译器正在为您创建一个额外的临时变量。您的name变量是隐式__strong的,因此编译器会插入一个临时变量,__autoreleasing并为您复制周围的值。NSString您可以通过明确声明为自动释放来避免这种情况。正如 rckoeness 所说,你也不需要init它,因为它scanUpToString:intoString:正在为你做这件事(这就是为什么它必须__autoreleasing放在首位)。(有关更多信息,请参阅http://developer.apple.com/library/mac/ipad/#releasenotes/ObjectiveC/RN-TransitioningToARC/_index.html)。

所以,总的来说,我认为你实际上希望你的代码看起来像这样:

NSScanner *scanner = [[NSScanner alloc] initWithString:query];
[scanner setCharactersToBeSkipped:[NSCharacterSet characterSetWithCharactersInString:@"&?"]];

NSString __autoreleasing *parameterString = nil;
while ([scanner scanUpToString:ampersand intoString:&parameterString]) 
{
    NSScanner *parameterScanner = [[NSScanner alloc] initWithString:parameterString];

    NSString __autoreleasing *name = nil;
    [parameterScanner scanUpToString:isEqual intoString:&name];

    NSString *value = [parameterString substringFromIndex:([name length] + 1)];
    [parameters setObject:value forKey:name];
}

希望有帮助!


我有另一个想法,也许name只是一个红鲱鱼。泄漏将显示分配发生的位置,但name在此循环之外继续存在,当它添加到parameters. 我假设它是一个NSMutableDictionary或类似的,基于选择器。如果我是你,我会确认name实例没有被泄露,因为该字典(或后来从字典中读取这些键的东西)正在泄露。

于 2012-01-16T13:18:48.920 回答
4

我一直在处理完全相同的问题。我的解决方案是根据应用到它的迭代次数来重置 NSScanner 对象。换句话说,每次运行扫描仪完整性测试时,我都会增加一个值,然后基于该值重新创建扫描仪对象并将当前位置从前一个扫描仪应用到新扫描仪。每次创建新版本的扫描仪时,我都会放置一个 @autoreleasepool 标记。

我以这种方式接近它的原因是因为 NSScanner 只是一个内存猪,并且在循环完成之前不会释放。我只通过活动查看器而不是任何工具验证了这一点。(我在 Mac OS X 应用程序设置中测试过)

享受!

NSUInteger currentLocation = 0;
while (currentLocation < [dehyphenatedText length])
{
@autoreleasepool
{
    NSUInteger iterations = 0;
    NSScanner * scanner = [NSScanner scannerWithString:dehyphenatedText];
    [scanner setCharactersToBeSkipped: nil];
    [scanner setScanLocation: currentLocation];
    while (([scanner scanLocation] < [dehyphenatedText length]) && (iterations < 15000))
    {
    NSString * found=nil;
    [scanner scanCharactersFromSet:inverted intoString:&found];
    if ((found != nil) && ([found length] > 0))
    {
               // Some code to process the results
            }
        found = nil;
        if ([scanner scanLocation] < [dehyphenatedText length])
        {
           [scanner scanCharactersFromSet: whiteSpaceAndMore intoString:nil];
        }

        iterations ++;
    }
    }
currentLocation = [scanner scanLocation];
 }
于 2012-11-26T14:57:17.383 回答
1

没有理由初始化name变量

    NSScanner *scanner = [[NSScanner alloc] initWithString:query];
    [scanner setCharactersToBeSkipped:[NSCharacterSet characterSetWithCharactersInString:@"&?"]];
    NSString *parameterString = [NSString new];
    while ([scanner scanUpToString:ampersand intoString:&parameterString]) 
    {
        NSScanner *parameterScanner = [[NSScanner alloc] initWithString:parameterString];

        NSString *name = nil;
        [parameterScanner scanUpToString:isEqual intoString:&name];

        NSString *value = [parameterString substringFromIndex:([name length] + 1)];
        [parameters setObject:value forKey:name];


    }
于 2012-01-11T14:00:12.713 回答
0

使用自动释放的初始化程序怎么样?

// [NSScanner scannerWithString:]
// and
// [NSString string]

NSScanner *scanner = [NSScanner scannerWithString:query];
[scanner setCharactersToBeSkipped:[NSCharacterSet characterSetWithCharactersInString:@"&?"]];

NSString *parameterString = [NSString string];
while ([scanner scanUpToString:ampersand intoString:&parameterString]) 
{
    NSScanner *parameterScanner = [NSScanner scannerWithString:parameterString];

    NSString *name = [NSString string];
    [parameterScanner scanUpToString:isEqual intoString:&name];

    NSString *value = [parameterString substringFromIndex:([name length] + 1)];
    [parameters setObject:value forKey:name];
}
于 2012-01-20T10:21:52.527 回答