Проблема с NSURLConnection и местоположением - PullRequest
1 голос
/ 11 февраля 2010

Я использую менеджер местоположений для генерации данных о местоположении, которые я отправляю в URL для загрузки данных Когда я в первый раз вызываю диспетчер местоположений, он корректно возвращает текущее местоположение, и на основе текущего местоположения я могу получить данные из URL.

Однако, когда я пытаюсь получить текущее местоположение во второй раз, я получаю EXC_BAD_EXCESS.

Когда я пытаюсь отладить с NSZombieEnabled, он показывает FirstViewController.recievedData как зомби в методе didReceiveResponse. (см. отмеченный код ниже)

Я продолжил копать и обнаружил, что после разрыва исходного соединения устанавливается неизвестное соединение, а затем он пытается получить доступ к receivedData, который уже освобожден.

Информация заголовочного файла: `

#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;

`

Код файла .m:

// запуск менеджера:

   [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

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

// и методы делегата

 #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];
        }

Пожалуйста, помогите. Я застрял.

Ответы [ 5 ]

3 голосов
/ 11 февраля 2010

У вас есть систематическая проблема с неправильным доступом к свойствам вашего класса. Свойства не будут автоматически сохраняться и освобождаться, если вы не используете self.propertyName для принудительного вызова методов доступа. Например:

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

Вы должны иметь:

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

У вас такая же проблема с recievedData и startingPoint. В подавляющем большинстве случаев, если вы используете синтезированные средства доступа, вам нужно вызывать release только для сохраненных свойств в dealloc. Использование аксессуаров прояснит вашу проблему с зомби.

Не зная, где происходит EXC_BAD_ACCESS, я не могу сказать однозначно, но поскольку эта ошибка часто возникает при отправке сообщений о несуществующем объекте, я могу сказать, что весьма вероятно, что вы обойдете средства доступа к свойству и освободите их вручную, вероятно, вызывает отправку кода в свойство nilled.

Исправьте доступ и посмотрите, решит ли это проблему.

Edit01:

TechZen проблема устранена на 50%. мое приложение прекрасно работает в режим отладки, но когда я вытаскиваю кабель и запускается снова, он падает. проблема, конечно, с местоположением менеджер . но мне не понятно сохраняет и освобождает место менеджер . Вы можете помочь мне

Я сделаю удар. Для управления памятью:

  1. Всегда доступ к вашему self.locationManager с использованием обозначение self-dot-propertyName для убедитесь, что вы используете механизм удержания / высвобождения сгенерированные аксессоры.
  2. Никогда не называйте релиз на любой собственности кроме как в методе dealloc. Если вы используете обозначение собственной точки и установить свойство для сохранения, все но конец жизни релиз обрабатывается автоматически для вас. это включает в себя времена, когда вы ноль свойство или установить его для другого объекта.
  3. Если сомневаешься, не отпускай. В последнем случае легче устранить утечку памяти, чем отследить ошибку, вызванную объектом, который исчезает в случайных точках в коде, потому что его счетчик сохранения искажен. Попытка предотвратить утечку, когда вы изучаете среду, является формой преждевременной оптимизации, которая вызывает больше проблем, чем предотвращает.

Я отмечаю, что в вашем методе locationManager:didUpdateToLocation:fromLocation: вы фактически не запрашиваете locationManager, переданный методу, а вместо этого запрашиваете свойство класса self.locationManager. Это может быть или не быть проблемой, но лучше использовать переданный менеджер, чтобы убедиться, что вы действительно запрашиваете обновленный экземпляр менеджера. Я также не считаю необходимым повторно уничтожать и воссоздавать менеджер местоположения. Я думаю, что вы можете инициализировать его один раз и сохранить его (проверьте документы по этому вопросу).

Если очистка ссылок на ваши свойства и использование прошедшего менеджера не помогают, я предлагаю вам опубликовать новый вопрос с исправленным кодом. В этот момент у вас законно появится новая проблема, и, кроме того, нам нужно увидеть очищенный код, чтобы определить проблему.

Edit02:

(на основе нового кода)

Вам не нужно автоматически высвобождать свойство self.locationManager здесь:

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

Вы используете autorelease только тогда, когда создаете объект и в своем классе, а затем отправляете его другому классу. Вы никогда не выпускаете автоматически свойства класса.

Вы должны прекратить попытки освободить ваши объявленные свойства. Вы никогда не освобождаете свойства, определенные с помощью retain, за исключением метода dealloc. Вы наступаете на свойства, созданные аксессорами, которые автоматически поддерживают счет сохранения.

Вы все еще не используете средства доступа последовательно. Это:

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] ;

должно быть:

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] ;

и это:

locationManager.delegate = nil;

должно быть:

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

Вам необходимо отследить все ссылки на все объявленные вами свойства и прикрепить self. к каждому (ожидайте, что внутри пользовательского средства доступа к свойствам вы, кажется, не используете его - что хорошо в этом случае.)

Я сильно подозреваю, что ваша проблема заключается в том, что вы не нуждаетесь в сохранении свойства self.locationManager.Возможно, вы заставляете менеджера местоположения исчезать наугад.

Вы все еще не используете переданного менеджера в locationManager:didUpdateToLocation:fromLocation: Я предлагаю вам сделать это или хотя бы проверить, что переданный менеджер - это тот же объект, что и ваш self.locationManager.просто замените self.locationManager на manager.

1 голос
/ 11 февраля 2010

Я не смог найти источник вашей проблемы, но у вас есть утечка в

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

вы должны использовать

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

вместо.

Редактировать: Загрузите Charles Web Proxy, проверьте, какие соединения вы делаете, какие ответы вы получаете, и, возможно, у нас будет лучшая идея.

Редактировать после комментариев: Автоматически сгенерированное свойство средства доступа, определенное для сохранения, автоматически сохраняет переданный объект и освобождает его, когда для свойства установлено значение nil / или release Таким образом, он выполняет свою работу, но ВАША задача отслеживать управление памятью переданного объекта. Итак, да, исходный код выше имеет УТЕЧКУ, и вы должны выполнить свою работу и ВЫПУСТИТЬ / АВТОРИЗАЛИ свой объект ALLOCATED, который в этом случае будет [[CLLocationManager alloc] init].

Редактировать: Я не знаю, как этот комментарий может получить -1. Это простое управление памятью. Ответы в этой теме все согласны с тем, что это правильный пост: iPhone: утечка или нет

1 голос
/ 11 февраля 2010

Вы освобождаете recceiveData в конце соединения, но не устанавливаете указатель на ноль - он все равно будет указывать на то место, где раньше находился recceiveData.

Вместо

[recievedData release];

попробовать

self.recievedData = nil;

Надеюсь, это поможет,

Sam

1 голос
/ 11 февраля 2010

Кто-то думает, что вы, безусловно, поступаете неправильно: вам нужно выделить receivedData, прежде чем начинать NSURLConnection. Он будет разветвляться в фоновом режиме, когда вы его выделите / инициализируете, поэтому receivedData должен быть готов до, а не после.

0 голосов
/ 12 февраля 2010

Я не уверен, в чем была проблема. но когда я сравнивал пример Apple LocateMe, я вижу locatiomManager.delegate = nil; Это полностью решает проблему.

...