首页 文章

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

提问于
浏览
2

在我的应用程序中,我需要知道用户位置,然后从数据库加载数组 . 所以我创建了一个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;
}

并在viewDidAppear中,如果我的表视图为空,我开始更新用户位置:

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

}

现在,当gps找到用户位置时,它会调用委托方法:

- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
    NSLog(@"DID UPDATE LOCATIONS");
    [self.gps stopUpdatingLocation];
    if (!didUpdateLocation) {
        didUpdateLocation = YES;
        currentLocation = [locations lastObject];
        [self performSelectorOnMainThread:@selector(loadDistance) withObject:nil waitUntilDone:YES];
        [self caricaLocaliInOrdineAlfabetico:NO];
    }
}

问题在于委托方法导致SOMETIMES(并非总是),即使我要求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

3 回答

  • 1

    你的前提是有缺陷的 .

    当您首次要求更新位置时,位置管理会产生非常糟糕的结果,并需要一段时间才能安定下来并开始提供良好的结果 .

    您不能只读取一个位置,并认为它是一个准确的位置 . 大多数情况下,这将是非常不准确的 .

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

    接下来,您将开始获得水平精度非常差的读数 . 前几个读数可能会偏离一公里或更多 . 您需要进行更新,直到获得足够准确的更新为止 . (精度读数实际上是一个半径值,表示读数中可能存在的误差 . )

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

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

  • 0

    它不是一个bug .

    didUpdateLocations:在调用stopUpdatingLocation后可以继续调用,所以你应该为此编写代码 .

  • 2

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

    在.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];
    }
    

    正如DuncanC所说,从gps获取的第一个位置可能不准确,因此我从horizontalAccuracy和verticalAccuracy计算平均精度,以提高当前位置的准确性 .

相关问题