2012-06-29 23 views
1

我正在制作一个应用程序,它在地图上显示用户以及多个餐馆列表。当用户点击一个引脚时,它将存储注释中的坐标,并将它们与用户进行比较以确保它们不同。一旦确定它们不同,它会将商家的坐标以及用户的坐标发送给Google以请求指示。代码工作正常,但为了做到这一点,我必须以导致内存泄漏的方式声明一些变量。我希望清理代码并了解我犯了什么错误以及应该如何处理这些问题。关于MKAnnotation和NSString内存泄漏问题的建议

下面是我的代码,用于从被点击的注释中获取坐标。如果我尝试初始化selectedAnnotation并通过放入selectedAnnotation = [[MapLocation alloc] init];来分配内存viewDidLoad然后它仍然显示为内存泄漏。作为参考,selectedAnnotation是一个MapLocation(符合MKAnnotation)变量,作为一个属性我有它(nonatomic,retain)和@synthesize(d)。

我认为只要我将它分配到内存中,只要我在viewDidUnload中将它的值设置为nil,并在dealloc中释放它,即不存在内存问题。我错过了什么?下面是当我在viewDidLoad中为selectedAnnotation分配内存以及下面提供的代码时,我的内存泄漏的屏幕截图。如果我已经分配了内存,并检查变量是否存在,为什么它会再次为变量分配内存?这种情况发生在我点击的任何餐馆的销售点上,但显然不在用户的销售点上,因为在这种情况下我有代码释放它。

enter image description here

-(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,但同样的问题仍然存在。

下面是放置用户PIN的代码。

- (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; 
    } 
} 

感谢您的任何和所有帮助/建议/意见。我真的很想了解我做错了什么,因为我认为我对它有很好的把握。

回答

2

didSelectAnnotationView,你有这样的代码:

selectedAnnotation = nil; 
[selectedAnnotation release]; 

这会导致内存泄漏,因为你设置selectedAnnotationnil然后调用它release

release的呼叫什么也不做,因为selectedAnnotationnil在那一点上,而nil的呼叫什么都不做。这意味着已分配的内存永远不会被释放,但由于指针变量已被设置为nil,因此当再次调用didSelectAnnotationView时,代码会分配一​​个新对象。

你应该切换两个语句的顺序(调用release第一然后设置为nil)。

然而,你并不需要的Alloc一个新的对象只是为了保住了“选择的注释”的参考。

声明一个常规伊娃(不是保留属性),只是设置它等于所选的注释应该工作。

另外,地图视图已经有一个名为selectedAnnotations的属性,您应该可以使用它(因此您不需要维护自己的ivar或属性)。地图视图的属性是NSArray,但始终包含0或1个对象。要确保其count索引0



访问对象在fetchedData前检查,必须引起不必要alloc调用几个内存泄漏。
它们不是必需的,因为在调用alloc之后,您直接为刚刚分配内存的指针分配一个新的引用。

例如,userLocationalloc'd之前的for循环,但然后在循环内您直接将该变量指向results阵列中的对象。

这意味着最初为userLocation分配的内存会被放弃而不参考它。当您尝试拨打userLocation上的release时,它正尝试释放未由fetchedData中的代码分配的对象。

要修复至少userLocation,只是声明变量和不alloc/init/release它。

变量addresstypeNSString)有类似的问题。

+0

实际上,对于selectedAnnotation,即使在修复nil/release的顺序之后,您仍然会遇到问题。原因与userLocation的原因相同(在alloc重新分配指针后)。因此,对于selectedAnnotation,只需声明一个ivar并且不要分配/ init /释放它(或者,更好的方法就是使用地图视图的selectedAnnotations属性)。 – Anna

+0

感谢您对'self.mapView.selectedAnnotation'属性的关注,我已经用它无缝地替换了我自己的所有属性,非常感谢。 就字符串的所有'alloc'调用而言,我已经能够删除其中的2个,但是'NSString * address = [[NSString alloc] initWithString:[userLocation objectForKey:@“formatted_address”]]; '我必须分配我猜,因为我把它的价值发送到'MKAnnotations'字幕。如果我没有'alloc'或者在for循环之后尝试'释放'',我就会崩溃:' - [CFString stringByStandardizingWhitespace]:发送到释放实例的消息' –

+0

这表明一些memoryUserLocation存在内存管理问题。还要确保MapLocation的字幕属性被定义为'copy'(不是'assign'或'retain')。 – Anna