2012-06-29 22 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