要解析URL的部分查询字符串,我使用此方法:
NSScanner *scanner = [[NSScanner alloc] initWithString:query];
[scanner setCharactersToBeSkipped:[NSCharacterSet characterSetWithCharactersInString:@"&?"]];
NSString *parameterString = [NSString new];
while ([scanner scanUpToString:ampersand intoString:¶meterString])
{
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];
究竟是什么泄漏,我该如何解决?
答案 0 :(得分: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:¶meterString])
{
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:¶meterString])
{
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
实例没有被泄露,因为该字典(或后来从字典中读取这些键的东西)被泄露。
答案 1 :(得分: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];
}
答案 2 :(得分:1)
没有理由初始化name
变量
NSScanner *scanner = [[NSScanner alloc] initWithString:query];
[scanner setCharactersToBeSkipped:[NSCharacterSet characterSetWithCharactersInString:@"&?"]];
NSString *parameterString = [NSString new];
while ([scanner scanUpToString:ampersand intoString:¶meterString])
{
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];
}
答案 3 :(得分:0)
使用自动释放的初始化程序怎么样?
// [NSScanner scannerWithString:]
// and
// [NSString string]
NSScanner *scanner = [NSScanner scannerWithString:query];
[scanner setCharactersToBeSkipped:[NSCharacterSet characterSetWithCharactersInString:@"&?"]];
NSString *parameterString = [NSString string];
while ([scanner scanUpToString:ampersand intoString:¶meterString])
{
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];
}