views:

493

answers:

5

Hello all ,

I am using the location manager to generate location data that I send in a URL to down load data. The first time I call the location manager, it correctly returns the current location and based on the current location I can fetch the data from the URL.

However, when I attempt to retrieve the current location a second time, I receive a EXC_BAD_EXCESS.

When I try debugging with NSZombieEnabled it shows me FirstViewController.recievedData as a zombie in the didReceiveResponse method. (see marked code below)

I dug further and I found that after releasing the initial connection an unknown connection is established and then it tries to access receivedData which is already released .

The header file info :`

#import <CoreLocation/CoreLocation.h>
define SECS_OLD_MAX 1
@interface FirstViewController : UIViewController<CLLocationManagerDelegate> {
    UIActivityIndicatorView *spinner;
    CLLocationManager *locationManager;
    CLLocation *startingPoint;
    UIButton *relocateMe;
    NSMutableData *receivedData;
    NSString *lat;
    NSString *lon;
}
@property (nonatomic, retain) IBOutlet UIActivityIndicatorView *spinner;
@property (nonatomic, retain) CLLocationManager *locationManager;
@property (nonatomic, retain) CLLocation *startingPoint;
@property (nonatomic, retain) IBOutlet UIButton *relocateMe;
@property (nonatomic, retain) NSMutableData *receivedData;
@property (nonatomic, retain) NSString *lat;
@property (nonatomic, retain) NSString *lon;

`

The .m file code :

//starting the manger :

   [spinner startAnimating];
**EDIT**************************ADDED IN THE AUTORELEASE POOL BY HIB********************************
    self.locationManager = [[[CLLocationManager alloc] init]autorelease];
    // Detecting the user device
    NSString *currentDevice =  [[UIDevice currentDevice] model];
    // if its iPhone then locate the current lattitude and longitude
    if([currentDevice isEqualToString:@"iPhone"] || [currentDevice isEqualToString:@"iPhone 3G"] || [currentDevice isEqualToString:@"iPhone 3G S"]){
        DLog(@"I have identified the device as an iPhone");
        if(locationManager.locationServicesEnabled == YES){
            DLog(@"ok now the location manager gets the property");
            locationManager.delegate = self;
            // This is the most important property to set for the manager. It ultimately determines how the manager will
            // attempt to acquire location and thus, the amount of power that will be consumed.
            locationManager.desiredAccuracy = kCLLocationAccuracyBest;
            // Once configured, the location manager must be "started".
            [locationManager startUpdatingLocation] ;
        }else {
            UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Oops!" 
                                                            message:@"Please enable location servies"
                                                           delegate:nil
                                                  cancelButtonTitle:@"OK" 
                                                  otherButtonTitles:nil];
            [alert show];
            [alert release];
        }
    }
    //if its iPod then fetch the city based restaurants
    else if([currentDevice isEqualToString:@"iPod touch"] || [currentDevice isEqualToString:@"iPod touch 2G"]){
    }
    else if([currentDevice isEqualToString:@"iPhone Simulator"]){
       //TechZen says: there appears to be some code missing here, not sure if its relevant
    }

//didupdatetolocation method

  - (void)locationManager:(CLLocationManager *)manager
        didUpdateToLocation:(CLLocation *)newLocation
               fromLocation:(CLLocation *)oldLocation {
        // store the location as the "best effort"
        DLog(@"Lat = %g Long = %g",newLocation.coordinate.latitude,newLocation.coordinate.longitude);
        NSDate *eventDate = newLocation.timestamp; 
        NSTimeInterval howRecent = [eventDate timeIntervalSinceNow];
        DLog(@"NSTIME INTERVAL = %i",howRecent);
        //Is the event recent and accurate enough ?
        if (abs(howRecent) < SECS_OLD_MAX) {
            self.lat = [NSString stringWithFormat:@"%g",newLocation.coordinate.latitude];
            self.lon = [NSString stringWithFormat:@"%g",newLocation.coordinate.longitude];
            [[NSUserDefaults standardUserDefaults] setObject:lat forKey:@"LATITUDE"];
            [[NSUserDefaults standardUserDefaults] setObject:lon forKey:@"LONGITUDE"];
        DLog(@"inside Lat = %g Long = %g",newLocation.coordinate.latitude,newLocation.coordinate.longitude);
        self.startingPoint = newLocation;
        [locationManager stopUpdatingLocation];
**EDIT********************************REMOVED BY HIB******************************
        self.locationManager = nil; 
        [locationManager release];  
**EDIT********************************REMOVED BY HIB******************************

**ADDED BY HIB********************************************
        locationManager.delegate = nil; 
**ADDED BY HIB********************************************
        @try {
            //passing the parameter for more condition
            self.lat = [NSString stringWithFormat:@"%g",startingPoint.coordinate.latitude];
            self.lon = [NSString stringWithFormat:@"%g", startingPoint.coordinate.longitude];
            NSString *string2 = [[NSString alloc] initWithFormat:@"%@/Service.asmx/someMethod?lat1=%g&lon1=%g&recordSize=0"
                                 ,[[NSUserDefaults standardUserDefaults] stringForKey:@"textEntry_key"],startingPoint.coordinate.latitude,startingPoint.coordinate.longitude];
            NSURL *url = [[NSURL alloc] initWithString:string2];
            [string2 release];
            NSMutableURLRequest* request2=[NSMutableURLRequest requestWithURL:url];
            [request2 setHTTPMethod:@"GET"]; 
            [request2 setTimeoutInterval:25.0];
            [[NSURLCache sharedURLCache] setMemoryCapacity:0];
            [[NSURLCache sharedURLCache] setDiskCapacity:0];
            NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:request2 delegate:self];
            if (theConnection) {
                receivedData = [[NSMutableData data]retain];
            } else {
                // inform the user that the download could not be made
                UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Sorry !"
                                                                message:@"The server is not avaialable \n Please try againa later" 
                                                               delegate:nil
                                                      cancelButtonTitle:@"OK"
                                                      otherButtonTitles:nil];
                [alert show];
                [spinner stopAnimating];
            }
            [url release];
        }
        @catch (NSException * e) {
        }
        @finally {
        }
    }
    }

//and the delegate methods

 #pragma mark -
    #pragma mark connection methods
    - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
    {
        // this method is called when the server has determined that it
        // has enough information to create the NSURLResponse
        // it can be called multiple times, for example in the case of a
        // redirect, so each time we reset the data.
        // receivedData is declared as a method instance elsewhere

    **************************************the zombie is here *********************************
        [receivedData setLength:0];
    *****************************************************************************************
    }
    - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
    {
        // append the new data to the receivedData
        // receivedData is declared as a method instance elsewhere
        [receivedData appendData:data];
    }
    - (void)connection:(NSURLConnection *)connection
      didFailWithError:(NSError *)error
    {
        [spinner stopAnimating];
        // release the connection, and the data object
        [connection release];
        // receivedData is declared as a method instance elsewhere
        [receivedData release];
        // inform the user
        DLog(@"Connection failed! Error - %@ %@",
              [error localizedDescription],
              [[error userInfo] objectForKey:NSErrorFailingURLStringKey]);
        // alert the user in the inter face.
        UIAlertView* alert = [[UIAlertView alloc]initWithTitle:@"Sorry !"
                                                       message:@"The server is not available.\n Please try again later."
                                                      delegate:nil
                                             cancelButtonTitle:@"OK" 
                                             otherButtonTitles:nil];
        [alert show];
        [alert release];
    }

    - (void)connectionDidFinishLoading:(NSURLConnection *)connection
    {
        // do something with the data
        // receivedData is declared as a method instance elsewhere
        DLog(@"Succeeded! Received %d bytes of data",[receivedData length]);
        [spinner stopAnimating];
        // release the connection, and the data object
        if(receivedData == nil)
        {
            UIAlertView* alert = [[UIAlertView alloc]initWithTitle:@"Sorry !" 
                                                           message:@"The server is not available.\n Please try again later or select city." 
                                                          delegate:nil 
                                                 cancelButtonTitle:@"OK" 
                                                 otherButtonTitles:nil];
            [alert show];
            [alert release];
            [spinner stopAnimating];
        }
        else
        {
        //just parse and use the data 
        }
        [connection release];
        [receivedData release];
        }

Please help . I am stuck.

+1  A: 

One think you are certainly doing wrong: you need to allocate receivedData before you start the NSURLConnection. It will fork in the background right when you alloc/init it, so receivedData needs to be ready before, not after.

St3fan
but the apple docs http://developer.apple.com/Mac/library/documentation/Cocoa/Conceptual/URLLoadingSystem/Tasks/UsingNSURLConnection.html says this
hib
Yeah confusing. It also says "The download starts immediately upon receiving the initWithRequest:delegate: message."
St3fan
+1  A: 

I could not find the source of your problem but you have a leak in

self.locationManager = [[CLLocationManager alloc] init];

you should use

self.locationManager = [[[CLLocationManager alloc] init] autorelease];

instead.

Edit: Download Charles Web Proxy, check what connections you are making, what responses you get, and maybe we'll have a better idea then.

Edit after Comments: The autogenerated accessor property defined to retain automatically retains the passed object, and releases it when you set the property to nil/or release. So it soes ITS job, but its YOUR job to keep track of memory management of the passed object. So, yes, the initial code above has a LEAK, and you should do your job and RELEASE/AUTORELEASE your ALLOCATED object, which in this case happens to be [[CLLocationManager alloc] init].

Edit : I don't know how this comment can get -1. It's simple memory management. The answers on this thread all agree this is a correct post: http://stackoverflow.com/questions/2252816/iphone-is-this-a-leak-or-not

ahmet emrah
thanks ahmet emrah
hib
but I am releasing it in didUpdateToLocation isn't it ?
hib
You are releasing location manager once: -1Making CLLocationmanager alloc : +1Assigning to a property with (retain) : +1So, you have a leak.
ahmet emrah
ok thanks for improving my concept .but dont you have any idea of the problem
hib
-1 you don't autorelease properties of a class. The autogenerated accessor of a property defined to retain will handle retention automatically.
TechZen
TechZen - if he doesn't autorelease it, and the setter retains it, it will have a retainCount of 2. When it's later released, it'll have a retain count of 1, and leak. I believe that Ahmet is correct.
Zach Wily
+1  A: 

Hi,

You are releasing recievedData at the end of a connection but are not setting your pointer to nil - it will still be pointing at where recievedData used to be.

Instead of

[recievedData release];

try

self.recievedData = nil;

Hope that helps,

Sam

deanWombourne
+1  A: 

You have a systematic problem with not properly accessing your class properties. The properties will not be automatically retained and released unless you use the self.propertyName to force the call to the accessors. For example:

[locationManager stopUpdatingLocation]; <-- direct access 
self.locationManager = nil; <-- access through generated accessor
[locationManager release]; <-- direct access again with release bypassing the automatic memory management

You should have:

[self.locationManager stopUpdatingLocation];
self.locationManager = nil;
//[locationManager release]; this line is now unneeded because the accessor handles it

You have the same problem with recievedData and startingPoint. In the vast majority of cases, if you use the synthesized accessors you only need to call release on retained properties in your dealloc. Use the accessors will clear up your zombie problem.

Without knowing where the EXC_BAD_ACCESS occurs I can't say definitively but since that error often occurs when messaging an non-existant object I can say that it is very likely that your bypassing of the property accessors and your releasing of them manually is probably causing the code to send to nilled property.

Fix the access and see if that resolves the problem.

Edit01:

TechZen the problem is removed 50 %. my application runs nicely in the debugging mode but when I pull out the cable and starts again it crashes . the problem is certainly with location manager . but I am not clear about retains and release of location manager . can you help me

I'll take a stab at it. For your memory management:

  1. Always access your self.locationManager using the self-dot-propertyName notation to make sure you utilize the retention/release mechanism of generated accessors.
  2. Never call release on any property except in the dealloc method. If you use the self-dot notation and have the property set to retain, all but the end of life release is handled automatically for you. This includes times when you nil the property or set it to another object.
  3. When in doubt, don't release. It's easier to fix a memory leak latter than it is to track down a bug caused by an object that disappears at random points in the code because its retain count is skewed. Trying to hard to prevent leaks when your learning the environment is a form of premature optimization that causes more trouble than it prevents.

I note that in your locationManager:didUpdateToLocation:fromLocation: method you don't actually query the locationManager passed to the method but instead query the class's self.locationManager property. This may or not be a problem but it is best to use the passed in manager to make sure you are in fact querying the manager instance that updated. I also don't think it necessary to destroy and recreate the location manager repeatedly. I think you can initialize it once and keep it around (check the docs on that.)

If cleaning up your property references and using the passed manager does not help, I suggest you post a new question with the cleaned up code. At that point you will legitimately have a new issue and besides we need to see the cleaned up code to spot the problem.

Edit02:

(Based on new code)

You don't need to autorelease your 'self.locationManager' property here:

self.locationManager = [[[CLLocationManager alloc] init]autorelease];

You only use autorelease when you create an object and in your class and then send it to another class. You never autorelease properties of class.

You need to stop trying to release your declared properties. You never release properties defined with retain except in the dealloc method. You are stepping on the properties generated accessors that maintain the retain count automatically.

Your are still not using the accessors consistently. This:

if(locationManager.locationServicesEnabled == YES){
    DLog(@"ok now the location manager gets the property");
    locationManager.delegate = self;
    // This is the most important property to set for the manager. It ultimately determines how the manager will
    // attempt to acquire location and thus, the amount of power that will be consumed.
    locationManager.desiredAccuracy = kCLLocationAccuracyBest;
    // Once configured, the location manager must be "started".
    [locationManager startUpdatingLocation] ;

should be:

if(self.locationManager.locationServicesEnabled == YES){
    DLog(@"ok now the location manager gets the property");
    self.locationManager.delegate = self;
    // This is the most important property to set for the manager. It ultimately determines how the manager will
    // attempt to acquire location and thus, the amount of power that will be consumed.
    self.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
    // Once configured, the location manager must be "started".
    [self.locationManager startUpdatingLocation] ;

and this:

locationManager.delegate = nil;

should be:

self.locationManager.delegate = nil; //<-- why are you doing this anyway? 

You need to track down all references to all your declared properties and affix self. to each one (expect inside a properties custom accessor which you don't seem to have use -- which is good in this case.)

I strongly suspect your problem is your unnecessary fiddling with the retention of the self.locationManager property. You maybe causing the location manager to disappear at random.

You are still not using the passed manager in locationManager:didUpdateToLocation:fromLocation: I suggest you do so or at least test that the passed manager is the same object as your self.locationManager. simply replace self.locationManager with manager.

TechZen
@TechZen the problem is removed 50 %. my application runs nicely in the debugging mode but when I pull out the cable and starts again it crashes . the problem is certainly with location manager . but I am not clear about retains and release of location manager . can you help me .
hib
I suggest you open a new question and post your new corrected code because I think you have a different problem now. In any case, we need to see the corrected to code in order to help. But see the edit for more detail. I'll take a shot at it.
TechZen
hey take a look to see the edits .
hib
A: 

I am not sure what was the actual problem . but when I was comparing the apple LocateMe example I see locatiomManager.delegate = nil; It solves the problem completely .

hib