iOS 7-CLLocationManager中可能存在的错误

iOS 7-CLLocationManager中可能存在的错误,ios,objective-c,cocoa-touch,cllocationmanager,Ios,Objective C,Cocoa Touch,Cllocationmanager,在我的应用程序中,我需要知道用户位置,然后从数据库加载一个数组。所以我创建了一个CLLocationManager的实例 @property (nonatomic,strong) CLLocationManager *gps; 在viewDidLoad中,我设置了gps的一些属性: - (void)viewDidLoad { [super viewDidLoad]; self.gps = [[CLLocationManager alloc] init]; self.gp

在我的应用程序中,我需要知道用户位置,然后从数据库加载一个数组。所以我创建了一个CLLocationManager的实例

@property (nonatomic,strong) CLLocationManager *gps;
在viewDidLoad中,我设置了gps的一些属性:

- (void)viewDidLoad
{
    [super viewDidLoad];
    self.gps = [[CLLocationManager alloc] init];
    self.gps.delegate = self;
    self.gps.desiredAccuracy = kCLLocationAccuracyBest;
}
如果我的表视图为空,则在ViewDidDisplay中开始更新用户位置:

- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
    if ([self.tableView numberOfRowsInSection:0] <= 0) {
        [self.gps startUpdatingLocation];
    }

}
问题在于委托方法,因为有时(并非总是)它会被调用多次,即使我要求gps停止更新位置。正如您所看到的,我还试图声明一个名为didUpdateLocation的BOOL来检查用户位置是否已被占用,但它似乎没有用。怎么可能

以下是调试日志:

2014-05-05 18:22:02.324 AppName[9529:60b] <FMDatabase: 0x16d5e740> executeQuery: SELECT * FROM locali
2014-05-05 18:22:02.483 AppName[9529:60b] DID UPDATE LOCATIONS
2014-05-05 18:22:06.002 AppName[9529:60b] CARICA LOCALI
2014-05-05 18:22:06.005 AppName[9529:60b] LOCALE INDEX: 170
2014-05-05 18:22:06.009 AppName[9529:60b] <FMDatabase: 0x16e6b040> executeQuery: SELECT * FROM locali ORDER BY distanza ASC
2014-05-05 18:22:06.068 AppName[9529:60b] DID UPDATE LOCATIONS
2014-05-05 18:22:09.784 AppName[9529:60b] CARICA LOCALI
2014-05-05 18:22:09.791 AppName[9529:60b] LOCALE INDEX: 170
2014-05-05 18:22:09.802 AppName[9529:60b] <FMDatabase: 0x16e72130> executeQuery: SELECT * FROM locali ORDER BY distanza ASC
2014-05-05 18:22:02.324应用程序名[9529:60b]执行程序:从本地选择*
2014-05-05 18:22:02.483 AppName[9529:60b]没有更新位置
2014-05-05 18:22:06.002应用名称[9529:60b]加共体地方
2014-05-05 18:22:06.005应用名称[9529:60b]地区索引:170
2014-05-05 18:22:06.009 AppName[9529:60b]执行:由distanza ASC从本地订单中选择*
2014-05-05 18:22:06.068 AppName[9529:60b]没有更新位置
2014-05-05 18:22:09.784应用名称[9529:60b]加勒比地区
2014-05-05 18:22:09.791 AppName[9529:60b]地区索引:170
2014-05-05 18:22:09.802应用程序名[9529:60b]执行:由distanza ASC从本地订单中选择*
这不是一个bug


DidUpdateLocation:在调用StopUpdateLocation后,可以继续调用一段时间,因此您应该为此编写代码。

您的前提有缺陷

当您第一次请求位置更新时,“位置管理”会给出非常糟糕的结果,需要一段时间才能稳定下来并开始给出好的结果

你不能只读取一个位置,然后假设它是一个准确的位置。大多数情况下,这将是非常不准确的

您需要检查位置更新上的时间戳,并拒绝超过几秒钟的更新。这是因为位置管理器缓存它在GPS上次运行时读取的位置,有时会给您一个超出日期数小时或数天的第一个位置读数,并且可能会大错特错

接下来,您将开始获得水平精度非常差的读数。前两个读数可能相差一公里或更长。你需要不断更新,直到你得到一个足够准确的更新。(精度读数实际上是一个半径值,指示读数中可能存在的误差。)

你需要对你的位置更新进行编码,以丢弃不准确的读数,如果你在合理的时间内没有得到满意的读数,最终也会超时(30秒是一个很好的起点)


只有当您得到的读数具有最近的时间戳且具有足够低的精度时,您才应处理该读数。此时,设置“didUpdateLocation”标志并停止位置更新。

我认为避免问题的最佳方法是使用计时器。现在,即使多次调用委托方法,也没有问题

在.h文件中

NSTimer *loadDistanceTimer;
在.m文件中

- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
    currentLocation = [locations lastObject];
    float averageAccuracy = (currentLocation.horizontalAccuracy + currentLocation.verticalAccuracy) / 2;
    NSLog(@"DID UPDATE LOCATIONS WITH %.0f AV_ACC",averageAccuracy);
    if (averageAccuracy < 20) {
        [self.gps stopUpdatingLocation];

        if (loadDistanceTimer == nil) {
            loadDistanceTimer = [NSTimer scheduledTimerWithTimeInterval:2.0
                                                                 target:self
                                                               selector:@selector(loadDistance)
                                                               userInfo:nil
                                                                repeats:NO];
        }
    }
}

- (void)loadDistance
{
    // while cycle here
    while (something) {
        //do something
    }

    loadDistanceTimer = nil;
    [self caricaLocaliInOrdineAlfabetico:NO];
}
-(无效)位置管理器:(CLLocationManager*)管理器更新位置:(NSArray*)位置
{
currentLocation=[locations lastObject];
浮点平均精度=(currentLocation.horizontalAccuracy+currentLocation.verticalAccuracy)/2;
NSLog(@“使用%.0f AV_ACC更新位置”,平均准确度);
if(平均精度<20){
[self.gps停止更新位置];
如果(loadDistanceTimer==nil){
loadDistanceTimer=[NSTimer scheduledTimerWithTimeInterval:2.0
目标:自我
选择器:@选择器(装载距离)
用户信息:无
重复:否];
}
}
}
-(空隙)荷载距离
{
//在这里骑自行车
当(某物){
//做点什么
}
loadDistanceTimer=nil;
[自身carcalocalinordinealfabetico:否];
}

正如Duncac所说,从gps获取的第一个位置可能不准确,因此我根据水平精度和垂直精度计算平均精度,以提高当前位置的精度。

主题外,但您应该指定@property的存储类型(通常使其成为(非原子的,强的)并将其作为self.gps访问,而不仅仅是gps。在第一段代码中,您只是声明了变量,在第二段中创建了实际的对象。非常不同的概念。你有权@wjl,但即使我用self.gps更改它,我也有同样的问题。虽然Martin是对的,但你应该预测(并计划)可能会有额外的位置更新,可能会滑入,这是一个旁白,请求
kCLOCATIONACURACYBEST
与没有
distanceFilter
但只获取第一个传入的筛选器之间的逻辑断开。如果您真的不关心
水平精度
,则可能需要将
所需的精度
设置为类似
KCallocationAccuracykilometer
的值。同样,您可以将
distanceFilter
设置为默认值
kCLDistanceFilterNone
之外的其他值。我也这么认为,这就是为什么我创建了一个BOOL来检查位置是否已被取用,但无论如何都会调用该方法(和if循环)。你知道原因吗?在这段代码中使用bool没有什么错,除非你无意中将它设置在其他地方。至少对于iPhone来说,它通常天生就有一个广泛的位置感,因为它知道它连接到哪个基站。所以你应该很快得到它。随后,它将需要做一个快速的Wifi扫描和休息的东西和/或定位四个GPS卫星,以提供更准确的措施,其最终和最准确的答案将是一个组合
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
    currentLocation = [locations lastObject];
    float averageAccuracy = (currentLocation.horizontalAccuracy + currentLocation.verticalAccuracy) / 2;
    NSLog(@"DID UPDATE LOCATIONS WITH %.0f AV_ACC",averageAccuracy);
    if (averageAccuracy < 20) {
        [self.gps stopUpdatingLocation];

        if (loadDistanceTimer == nil) {
            loadDistanceTimer = [NSTimer scheduledTimerWithTimeInterval:2.0
                                                                 target:self
                                                               selector:@selector(loadDistance)
                                                               userInfo:nil
                                                                repeats:NO];
        }
    }
}

- (void)loadDistance
{
    // while cycle here
    while (something) {
        //do something
    }

    loadDistanceTimer = nil;
    [self caricaLocaliInOrdineAlfabetico:NO];
}