2012-01-11 3 views
2

Чтобы разобрать часть строки запроса в качестве URL я использую этот метод:NSScanner scanUpToString утечки при использовании ARC

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]; 

Что именно происходит, и как я могу это решить?

+1

Вы видели эту утечку через инструмент «Утечки инструментов»? Если это так, спуститесь на нижнюю панель и измените опцию отображения с «Утечки» на «Циклы и корни», чтобы отобразить новые инструменты обнаружения цикла удержания. Это может показать, что цикл утечки отвечает за эту утечку в ARC. Кроме того, вы можете использовать функцию кучи в инструменте Allocations, чтобы определить, какие объекты точно просачиваются для каждого прохода над вышеуказанным кодом. –

+0

Переменная имени протекает, каждый передает вышеприведенный код. Любое решение, как это решить? – Thys

ответ

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]; 


    } 
+0

Он все еще протекает, когда я не запустил имя. – Thys

6

Я подозреваю, что имя на самом деле не течет, она просто не будет выпущен, когда вы думаете. Согласно ARC, я считаю, что scanUpToString:intoString: будет определяться аналогично методам с использованием NSError. Другими словами, требуется NSString * __autoreleasing *. Поэтому любое значение, переданное ему, фактически автореализуется и не будет выпущено до тех пор, пока текущий пул авторасчетов не будет исчерпан. Предполагая, что у вас нет других, усеиваемых вокруг, это произойдет, когда цикл цикла снова пройдет. Если использование памяти является проблемой для вас, то можно было бы поместить явный autorelease бассейн вокруг петли, поэтому объекты уходят сразу:

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 как автореализацию. Вам также не нужно init его, как сказал rckoeness, потому что 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 не просачиваются, потому что этот словарь (или что-то, что позже читает эти ключи из словаря) просачивается.

+0

Но если имя не протекает, почему инструменты показывают его как утечку? Вышеуказанные решения не решают (по показаниям приборов) утечку. – Thys

+0

Я добавил еще одно примечание, которое может объяснить это. Я также оставил исходный текст, поскольку оптимизация по-прежнему действительна. – extremeboredom

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]; 
} 
+0

Нет, как сказано в предыдущем ответе; автореализация не выполняется должным образом так или иначе. – Thys

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]; 
} 
Смежные вопросы