Я становлюсь более знакомым с инструментами для отладки, и его гораздо проще использовать, чем это первоначально показалось. Эта проблема была опубликована, поэтому я мог понять, почему она указывала на одну строку кода в качестве источника моей утечки, и каков был бы логический процесс ее устранения.
Ответ может принести пользу кому-то другому, пытающемуся использовать инструменты. Я не видел много деталей о том, как его использовать. (Существует хорошее руководство пользователя по приборам, но об этом.)
В этом случае 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:].
Я надеюсь, что здесь не так уж много кода для просмотра, но я в тупик. Кроме того, мне любопытно, есть ли какие-либо очевидные недоразумения относительно поведения установщиков свойств. (Все сеттеры и геттеры синтезированы.)