Инструменты, указывающие на утечку в структуре - найденная утечка была где-то еще (почему?) - PullRequest
0 голосов
/ 25 февраля 2012

Я становлюсь более знакомым с инструментами для отладки, и его гораздо проще использовать, чем это первоначально показалось. Эта проблема была опубликована, поэтому я мог понять, почему она указывала на одну строку кода в качестве источника моей утечки, и каков был бы логический процесс ее устранения.

Ответ может принести пользу кому-то другому, пытающемуся использовать инструменты. Я не видел много деталей о том, как его использовать. (Существует хорошее руководство пользователя по приборам, но об этом.)

В этом случае Instruments указывал на повторяющуюся утечку в строке, возвращающей JSONValue (тип NSDictionary) из структуры SBJSON. Я пытался различными способами изолировать проблему (см. Ниже), и в каждом случае Instruments по-прежнему указывали на строку, возвращающую объект словаря JSON.

Еще одна вещь, которую я попробовал, - NSJSONSerializer, доступный в iOS 5. Инструменты снова указали на ту же линию. Очевидно, инструменты вводили меня в заблуждение. (Почему? Что я могу сделать, чтобы избежать / улучшить это?)

Короче говоря, проблема была не в том, куда указывали инструменты, а в том, что содержало эту строку. В этом случае было два свойства, значения которых были получены из десериализованного словаря JSON, которые не были выпущены.

Я пришел к этому выводу методом проб и ошибок, закомментировав код и заменив возвращаемые значения буквальными строками. (Извини, если я испугаюсь, Стиг!)


Более раннее обновление

Вот ревизия моего поста. Я считаю, что я сузил проблему до сбоя возвращаемой строки SBJSON, которая не была автоматически выпущена. Я хотел бы еще кое-что подумать о том, имеет ли смысл мой процесс исключения, какой вывод я должен сделать и как решить эту проблему.

Оригинальный вопрос ниже. Я сосредоточился на этом коде:

    NSString *resultsGeocodeLiteralString = @"{... the rest of the string ...}";        
    NSAutoreleasePool *aPool = [[NSAutoreleasePool alloc] init];
/*1*/     NSDictionary *dico = [resultsGeocodeLiteralString JSONValue]; // <-- Instruments still points here.
/*2a*/    self.resultsGeoCode = [[NSDictionary alloc] initWithDictionary:dico copyItems:YES];
/*2b*/    [self.resultsGeoCode release];
    [aPool release];

Здесь я использую буквенную строку resultsGeocodeLiteralString, которая преобразуется в NSDictionary с помощью метода SBJSON JSONValue. Я проверил с помощью литеральной строки, чтобы отделить проблему от исходного входного аргумента и его управления памятью.

Не обращая внимания на пул автоматического выпуска, я попробовал этот код, глубоко скопировав результат JSONValue в свойство self.resultsGeoCode.

Инструменты указали, что утечка находится в строке, вызывающей метод JSONValue.

Это заставляет меня думать, что что-то не так с автоматическим выпуском. Поэтому я поместил этот код в короткий пул авто-релизов. Это не повлияло на результаты. Приборы показали одинаковую утечку, указывая на одну и ту же линию.


Оригинальный вопрос ниже:

Instruments указывает на этот код как на источник нескольких утечек. У меня закончились идеи о том, как решить эту проблему. Этот метод находится в объекте типа Geocoder

Во-первых, вот код. Прибор указывает на линию, помеченную / 1 /. Я пробовал глубокое копирование NSDictionary, возвращенного как JSONValue (/ 2a / и / 2b /), просто чтобы удалить проблему из возвращаемого объекта, но это не имело никакого значения .

- (NSString *) processResults:(NSString *) resultsGeoCodeString {

    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

/*1*/     self.resultsGeoCode = [resultsGeoCodeString JSONValue];
/*2a*/    self.resultsGeoCode = [[NSDictionary alloc] initWithDictionary:self.resultsGeoCode copyItems:YES];
/*2b*/    [self.resultsGeoCode release]; // It's retained twwice in the line above.

/* copy a stirng */
    self.previousStatusCode = [self.resultsGeoCode objectForKey:@"status"];

    if ([self.previousStatusCode isEqualToString:@"OK"] == YES) {

/* Break it down. Results might be a single object or an array of objects. If it's an array, just take the first one. */
        NSArray *address_components;
        NSDictionary *geom;
        if ([[self.resultsGeoCode objectForKey:@"results"] isKindOfClass:[NSArray class]]) {
            address_components = [[(NSArray *)[self.resultsGeoCode objectForKey:@"results"] objectAtIndex:0] objectForKey:@"address_components"];
            geom = [[[self.resultsGeoCode objectForKey:@"results"] objectAtIndex:0] objectForKey:@"geometry"];
        }
        else {
            address_components = [[[self.resultsGeoCode objectForKey:@"results"] objectAtIndex:0] objectForKey:@"address_components"];
            geom = [[self.resultsGeoCode objectForKey:@"results"] objectForKey:@"geometry"];
        }

/* deep copy the geometry */
        self.geometry = [[NSDictionary alloc] initWithDictionary:geom copyItems:YES];
        [self.geometry release]; // it is retained twice in the line above

/* copy a string */
        self.formattedAddress = [[[self.resultsGeoCode objectForKey:@"results"] objectAtIndex:0] objectForKey:@"formatted_address"];

/* copy the strings from specific types */
        NSArray *typesArray;
        for (NSDictionary *address_component in address_components) {
            if ([[address_component objectForKey:@"types"] isKindOfClass:[NSArray class]])
                typesArray = [address_component objectForKey:@"types"];
            else
                typesArray = [NSArray arrayWithObjects:[address_component objectForKey:@"types"], nil];

            for (NSString *componentType in typesArray) {

                if ([componentType isEqualToString:@"locality"]) 
                    self.city = [address_component objectForKey:@"long_name"];

                else if ([componentType isEqualToString:@"administrative_area_level_1"]) 
                    self.region = [address_component objectForKey:@"long_name"];

                else if ([componentType isEqualToString:@"country"]) {
                    self.country = [address_component objectForKey:@"long_name"];
                    self.countryCode = [address_component objectForKey:@"short_name"];
                }
                else if ([componentType isEqualToString:@"postal_code"]) 
                    self.postalCode = [address_component objectForKey:@"long_name"];

                else if ([componentType isEqualToString:@"street_number"]) 
                    self.streetNumber = [address_component objectForKey:@"long_name"];

                else if ([componentType isEqualToString:@"route"]) 
                    self.route = [address_component objectForKey:@"long_name"];

            }
        }
    }

    [pool release];

/* a retained property set to nil -- not needed anymore */
    self.resultsGeoCode = nil;

/* return a string */    
    return self.previousStatusCode;
}

Все свойства NSString имеют атрибут copy. Все свойства NSDictionary имеют атрибут retain. Вы можете видеть, что я делаю глубокую копию любых элементов словаря.

Вот метод dealloc:

- (void) dealloc {
/* NSString properties with copy attribute */
    self.streetNumber = nil;
    self.route = nil;
    self.city = nil;
    self.region = nil;
    self.country = nil;
    self.postalCode = nil;
    self.previousStatusCode = nil;
    self.region = nil;

/* I have tried it with and without these releases  of the dictionary properties */
    if (location_) [location_ release];
    if (geometry_) [geometry_ release];
    if (regionGeometries_) [regionGeometries_ release];
    if (resultsGeoCode_) [resultsGeoCode_ release];

/* I would have thought these would be sufficient to release the retained properties */
//    self.location = nil;
//    self.geometry = nil;
//    self.regionGeometries = nil;
//    self.resultsGeoCode = nil;

    [super dealloc];
}

Инструменты говорят, что объект утечки - это NSCFString, а ответственный кадр (я не знаю, что это) - - [NSPlaceholderString initWithBytes: length: encoding:].

Я надеюсь, что здесь не так уж много кода для просмотра, но я в тупик. Кроме того, мне любопытно, есть ли какие-либо очевидные недоразумения относительно поведения установщиков свойств. (Все сеттеры и геттеры синтезированы.)

1 Ответ

1 голос
/ 25 февраля 2012

Это не 100% ответ, потому что мы не знаем, что будет дальше, а что было раньше.

Я не уверен, но есть некоторые вещи, которые вам не нужно делать:
Во-первых:

/*2a*/    self.resultsGeoCode = [[NSDictionary alloc] initWithDictionary:[resultsGeoCodeString JSONValue] copyItems:YES];

вместо этих 3 строк. Если вам не нужен аргумент функции resultsGeoCodeString, вы можете освободить его.

Во-вторых:

self.geometry = [[NSDictionary alloc] initWithDictionary:geom copyItems:YES];
[self.geometry release];

Почему вы отпускаете его сразу после того, как вы выделили этот объект? Отпустите его после того, как вы сделали с этим объектом. То же, что и в предыдущем пункте.

И до [pool release]; добавить строку [self.resultsGeoCode release]; self.resultsGeoCode = nil; или ранее.

Я предлагаю вам использовать ARC вместо ручного подсчета удержания.

...