1

我正在使用位置管理器来生成我在 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];
        }

请帮忙 。我被困住了。

4

5 回答 5

3

您有一个系统性问题,无法正确访问您的类属性。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。在绝大多数情况下,如果您使用合成访问器,您只需要在您的 dealloc 中的保留属性上调用 release。使用访问器将清除您的僵尸问题。

在不知道发生在哪里的EXC_BAD_ACCESS情况下,我不能明确地说,但是由于在消息传递不存在的对象时经常发生该错误,我可以说您绕过属性访问器并手动释放它们很可能导致代码发送到无效的财产。

修复访问权限,看看是否能解决问题。

编辑01:

TechZen 解决了 50% 的问题。我的应用程序在调试模式下运行良好,但是当我拔出电缆并重新启动时它崩溃了。问题肯定出在位置管理器上。但我不清楚位置管理器的保留和释放。你能帮助我吗

我会试一试。对于您的内存管理:

  1. 始终 self.locationManager使用 self-dot-propertyName 表示法访问您,以确保您利用生成的访问器的保留/释放机制。
  2. 切勿在方法中以外的任何属性上调用 release dealloc。如果您使用自点表示法并将属性设置为保留,则会自动为您处理除生命周期结束之外的所有释放。这包括您将属性设为 nil 或将其设置为另一个对象的时间。
  3. 当有疑问时,不要释放。修复内存泄漏比跟踪由在代码中随机点消失的对象引起的错误更容易,因为它的保留计数是倾斜的。在学习环境时努力防止泄漏是一种过早优化的形式,它造成的麻烦比它所防止的要多。

我注意到,在您的locationManager:didUpdateToLocation:fromLocation:方法中,您实际上并没有查询传递给该方法的 locationManager,而是查询了类的self.locationManager属性。这可能是也可能不是问题,但最好使用传入的管理器来确保您实际上正在查询更新的管理器实例。我也不认为有必要反复销毁和重新创建位置管理器。我认为您可以对其进行一次初始化并保留它(查看文档。)

如果清理您的属性引用并使用传递的管理器没有帮助,我建议您使用清理后的代码发布一个新问题。到那时,您将合法地遇到一个新问题,此外我们需要查看清理后的代码以发现问题。

编辑02:

(基于新代码)

您不需要在这里自动释放您的“self.locationManager”属性:

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

您仅在创建对象并在您的类中使用自动释放,然后将其发送到另一个类。你永远不会自动释放类的属性。

您需要停止尝试释放您声明的属性。除非在 dealloc 方法中,否则您永远不会释放使用 retain 定义的属性。您正在踩踏属性生成的访问器,这些访问器自动维护保留计数。

您仍然没有始终如一地使用访问器。这个:

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.locationManagermanager.

于 2010-02-11T14:46:05.370 回答
1

您在连接结束时释放 recievedData 但没有将指针设置为 nil - 它仍将指向 recievedData 曾经所在的位置。

代替

[recievedData release];

尝试

self.recievedData = nil;

希望有帮助,

山姆

于 2010-02-11T13:56:33.130 回答
1

有人认为您肯定做错了:您需要receivedData在启动NSURLConnection. 当你分配/初始化它时,它会在后台分叉,所以receivedData需要在之前准备好,而不是之后。

于 2010-02-11T13:45:27.450 回答
1

我找不到你的问题的根源,但你有泄漏

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

你应该使用

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

反而。

编辑:下载 Charles Web Proxy,检查您正在建立的连接,您得到的响应,也许那时我们会有更好的主意。

注释后编辑:定义为保留的自动生成的访问器属性会自动保留传递的对象,并在您将属性设置为 nil/或释放时释放它。所以它是它的工作,但它是你的工作来跟踪传递对象的内存管理。所以,是的,上面的初始代码有一个泄漏,你应该做你的工作并释放/自动释放你的 ALLOCATED 对象,在这种情况下恰好是[[CLLocationManager alloc] init].

编辑: 我不知道这个评论怎么会得到-1。这是简单的内存管理。该线程上的答案都同意这是一个正确的帖子: iPhone:这是否泄漏

于 2010-02-11T13:51:01.000 回答
0

我不确定实际问题是什么。但是当我比较苹果 LocateMe 示例时,我看到locatiomManager.delegate = nil;它完全解决了问题。

于 2010-02-12T10:55:17.217 回答