关于 MKAnnotation 和 NSString 内存泄漏问题的建议
Posted
技术标签:
【中文标题】关于 MKAnnotation 和 NSString 内存泄漏问题的建议【英文标题】:Advice on MKAnnotation and NSString memory leak issues 【发布时间】:2012-06-29 05:08:25 【问题描述】:我正在制作一个在地图上显示用户以及多个餐厅列表的应用。当用户点击一个图钉时,它会存储注释中的坐标,并将它们与用户进行比较,以确保它们不同。一旦确定它们不同,它就会将商家的坐标连同用户的坐标一起发送给 Google 以请求方向。代码工作正常,但为了做到这一点,我必须以导致内存泄漏的方式声明一些变量。我希望清理代码并了解我出错的地方以及应该处理的正确方法。
所以下面是我从被点击的注释中获取坐标的代码。如果我尝试初始化 selectedAnnotation 并通过放置 viewDidLoad
分配它的内存 selectedAnnotation = [[MapLocation alloc] init];
然后它仍然显示为内存泄漏。作为参考,selectedAnnotation
是一个 MapLocation(符合 MKAnnotation)变量,作为属性,我拥有它(非原子,保留)和 @synthesize(d)。
我以为只要在内存中分配,只要在viewDidUnload中将其值设置为nil并在dealloc中释放,就应该没有内存问题。我错过了什么?下面是我在 viewDidLoad 中为 selectedAnnotation 分配内存时内存泄漏的屏幕截图以及下面提供的代码。如果我已经分配了内存,并检查变量是否存在,为什么还要为变量分配内存?这发生在我点击的任何餐厅 pin 上,但显然不是在用户的 pin 上,因为在这种情况下我有释放它的代码。
-(void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view
//NSLog(@"Selected annotation view");
// if we don't have the place holder already allocated
// lazy load the MapLocation placeholder variable
if(!selectedAnnotation)
selectedAnnotation = [[MapLocation alloc] init];
// save the annotation clicked
selectedAnnotation = view.annotation;
// if the annotation selected was is the same as the user's location
if((selectedAnnotation.coordinate.latitude == savedUserLocation.coordinate.latitude) && (selectedAnnotation.coordinate.longitude == savedUserLocation.coordinate.longitude))
// set it to nil and release it
selectedAnnotation = nil;
[selectedAnnotation release];
我在使用以下方法时遇到了类似的内存问题。我从 Google 引入 JSON 数据,以提取用户位置的地址和坐标以显示在 AnnotationView 中。我创建了所有必要的数组和字典来访问信息,但是一旦我为它们分配内存并将它们的值分配给savedUserLocation,如果我尝试释放说NSDictionary变量userLocation,即使作为此方法中的最后一行代码,由于"[CFDictionary release]: message sent to deallocated instance 0x83ccb60"
,应用程序崩溃。我很确定这是因为我通过指针在savedUserLocation
中设置值,一旦内存被释放,信息就不再存在,那么将内存分配/释放到我可以访问的位置的正确方法是什么信息,而不会导致内存泄漏?我也尝试过使用autorelease
,但同样的问题仍然存在。
这是放置用户密码的代码。
- (void)fetchedData:(NSData *)responseData
//parse out the json data
NSError *error;
NSDictionary *json = [NSJSONSerialization
JSONObjectWithData:responseData //1
options:kNilOptions
error:&error];
NSArray *results = [json objectForKey:@"results"]; //2
NSUInteger counter = [results count];
NSDictionary *userLocation = [[NSDictionary alloc] init];
//NSString *address = [[NSString alloc] init];
for(NSUInteger i=0; i < counter; i++)
userLocation = [results objectAtIndex:i];
// 2) Get the funded amount and loan amount
NSString *address = [[NSString alloc] initWithString:[userLocation objectForKey:@"formatted_address"]];
NSArray *types = [userLocation objectForKey:@"types"];
NSDictionary *geometry = [userLocation objectForKey:@"geometry"];
NSDictionary *location = [geometry objectForKey:@"location"];
float lat = [[location objectForKey:@"lat"] floatValue];
float lon = [[location objectForKey:@"lng"] floatValue];
CLLocationCoordinate2D newCoordinates;
newCoordinates.latitude = lat;
newCoordinates.longitude = lon;
// count how many types there are
NSUInteger numberOfTypes = [types count];
NSString *type = [[NSString alloc] init];
for(NSUInteger j=0; j < numberOfTypes; j++)
type = [types objectAtIndex:j];
if([type rangeOfString:@"street_address" options:NSCaseInsensitiveSearch].location != NSNotFound)
NSLog(@"%@", address);
if(!savedUserLocation)
savedUserLocation = [[MapLocation alloc] init];
[savedUserLocation setTitle:@"You are here!"];
[savedUserLocation setSubtitle:address];
[savedUserLocation setCoordinate:newCoordinates];
// determine which location is closest to the user by calling this function
MapLocation *closestLocation = [self determineClosestLocationToUser:allLocations locationOfUser:savedUserLocation];
// send in the user location and the closest store to them to determine appropriate zoom level and
// to center the map between the two
[self determineMapCenterAndZoomLevelFromUser:savedUserLocation andClosestLocation:closestLocation];
if(!pinDropped)
// add the annotation to the map and then release it
[mapView addAnnotation:savedUserLocation];
pinDropped = true;
感谢您提供的任何帮助/建议/建议。我真的很想了解我做错了什么的具体细节,因为我认为我对它有相当不错的把握。
【问题讨论】:
【参考方案1】:在didSelectAnnotationView
,你有这个代码:
selectedAnnotation = nil;
[selectedAnnotation release];
这会导致内存泄漏,因为您将selectedAnnotation
设置为nil
并且然后 在其上调用release
。
对release
的调用什么都不做,因为此时selectedAnnotation
是nil
,而对nil
的调用什么也不做。这意味着已分配的内存永远不会释放,但由于指针变量已设置为 nil
,当再次调用 didSelectAnnotationView
时,您的代码会分配一个新对象。
你应该切换两个语句的顺序(调用release
first和then设置为nil
)。
然而,您不需要分配一个新对象来保持对“选定注释”的引用。
声明一个常规 ivar(不是保留属性)并将其设置为等于所选注释应该可以工作。
此外,地图视图已经有一个名为selectedAnnotations
的属性,您应该可以使用它(因此您不需要维护自己的 ivar 或属性)。地图视图的属性是 NSArray
,但总是包含 0 或 1 个对象。在访问索引 0 处的对象之前,请务必检查其 count
。
在fetchedData
中,您有几个由不必要的alloc
调用引起的内存泄漏。
它们不是必需的,因为在调用 alloc
之后,您会直接为刚刚为其分配内存的指针分配一个新引用。
例如,userLocation
在 for 循环之前是 alloc
,但在循环内部,您直接将该变量指向 results
数组中的对象。
这意味着最初分配给userLocation
的内存被废弃,不再引用它。当您尝试在userLocation
上调用release
时,它正在尝试释放一个未被fetchedData
中的代码分配的对象。
要至少修复userLocation
,只需声明变量而不是alloc
/init
/release
。
变量address
和type
(NSString
)有类似的问题。
【讨论】:
其实对于selectedAnnotation,即使修复了nil/release的顺序,还是会有问题。原因与 userLocation 的原因相同(在 alloc 之后重新分配指针)。所以对于 selectedAnnotation,只需声明一个 ivar 并且不要分配/初始化/释放它(或者,更好的是,只需使用地图视图的 selectedAnnotations 属性)。 感谢self.mapView.selectedAnnotation
属性的提醒,我已经用它无缝替换了我自己的所有属性,非常感谢。就字符串上的所有alloc
调用而言,我已经能够删除其中的两个,但我猜必须分配NSString *address = [[NSString alloc] initWithString:[userLocation objectForKey:@"formatted_address"]];
,因为我将它的值发送给MKAnnotations
字幕。如果我不 alloc
它,或者在 for 循环之后尝试 release
它,我会崩溃:-[CFString stringByStandardizingWhitespace]: message sent to deallocated instance
这表明 savedUserLocation 存在内存管理问题。还要确保 MapLocation 的 subtitle 属性定义为copy
(不是assign
或retain
)。
感谢您的所有帮助@Anna,我仔细检查了 subtitle 属性是copy
,任何其他会导致崩溃的想法,因为我没有alloc
address
变量?以上是关于关于 MKAnnotation 和 NSString 内存泄漏问题的建议的主要内容,如果未能解决你的问题,请参考以下文章