NSURLConnection和位置问题

时间:2010-02-11 13:34:40

标签: iphone nsurlconnection cllocationmanager nsmutableurlrequest

我正在使用位置管理器生成我在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 个答案:

答案 0 :(得分: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

recievedDatastartingPoint存在同样的问题。在绝大多数情况下,如果使用合成访问器,则只需要在dealloc中调用保留属性的释放。使用访问器将清除你的僵尸问题。

在不知道EXC_BAD_ACCESS出现在哪里的情况下,我无法明确地说出来,但是因为在发送一个不存在的对象时经常会发生这种错误,我可以说很可能绕过了属性访问器并释放了他们手动可能导致代码发送到nilled属性。

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

Edit01:

  

TechZen问题被删除了50%。   我的应用程序运行良好   调试模式,但当我拔出   电缆再次启动它崩溃。   问题肯定在于地理位置   经理 。但我不清楚   保留和释放位置   经理 。你能帮我吗

我会捅它。为了你的记忆管理:

  1. 始终访问您的 self.locationManager使用 self-dot-propertyName表示法 确保你使用 保留/释放机制 生成的访问者。
  2. 永远不要在任何财产上发布释放     除dealloc方法外。如果     你使用自我点符号和     将所有属性设置为保留     但是生命的终结是     自动处理。这个     包括你没有的时间     属性或将其设置为另一个对象。
  3. 如有疑问,请勿发布。后者修复内存泄漏比跟踪代码中随机点消失的错误更容易,因为它的保留计数是倾斜的。当你学习环境是一种过早优化的形式时,试图努力防止泄漏,这会导致比预防更多的麻烦。
  4. 我注意到在locationManager:didUpdateToLocation:fromLocation:方法中,您实际上并不查询传递给方法的locationManager,而是查询类的self.locationManager属性。这可能是一个问题,但最好是使用传入的管理器来确保您实际查询更新的管理器实例。我也认为没有必要反复销毁和重新创建位置管理器。我认为你可以初始化它并保留它(检查文档。)

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

    Edit02:

    (基于新代码)

    您无需在此处自动发布'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.locationManager替换为manager即可。

答案 1 :(得分:1)

有人认为你肯定做错了:你需要在开始receivedData之前分配NSURLConnection。当你分配/初始化它时,它会在后台进行分叉,因此receivedData需要在之前而不是之后准备好。

答案 2 :(得分:1)

我找不到问题的根源但是你有泄密

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

你应该使用

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

代替。

编辑:下载Charles Web Proxy,检查您正在建立的连接,获得的回复,以及我们当时可能会有更好的想法。

在评论后编辑:定义为保留的自动生成的访问者属性会自动保留传递的对象,并在将属性设置为nil /或release时将其释放。因此它支持ITS工作,但它的工作是跟踪传递对象的内存管理。所以,是的,上面的初始代码有一个LEAK,你应该做你的工作和RELEASE / AUTORELEASE你的ALLOCATED对象,在这种情况下碰巧是[[CLLocationManager alloc] init].

修改: 我不知道这个评论怎么能得到-1。这是简单的内存管理。这个帖子的答案都同意这是一个正确的帖子: iPhone: Is this a leak or not

答案 3 :(得分:1)

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

而不是

[recievedData release];

self.recievedData = nil;

希望有所帮助,

萨姆

答案 4 :(得分:0)

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