LocationManager定位、地址搜索相关痛点解决(Base on高德)

地图选址

项目中对定位、地址搜索等功能采用了LocationManager单例模式,关于单例的优点在此就不赘述了,这里主要想分享一下单例模式下LocationManager的两个痛点。

首先简单贴一下单例的实现:

static LocationManger *locationInstance;
+(LocationManger*)getInstanceWithDelegate:(id)delegate
{
    @synchronized(self) {
        if (!locationInstance) {
            locationInstance = [[LocationManger alloc] init];
            locationInstance.geocoder = [[CLGeocoder alloc] init];
        }
        if (delegate) {
            locationInstance.delegate = delegate;
        }
    }
    return locationInstance;
}

当然,更推荐这样的写法:

static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
      //TO DO
    });

接下来,开始分析痛点:

痛点一

项目中某页面需要同时用到定位当前地址+对某坐标进行逆地理编码验证两个功能,高德回调函数难以区分。
当前地址的逻辑大致如下(非完整代码):

//发起定位
[self.lManager startUpdatingLocation];
//定位回调里拿到坐标->发起逆地理编码
-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
    ...
    //构造AMapReGeocodeSearchRequest对象,location为必选项,radius为可选项
    AMapReGeocodeSearchRequest *regeoRequest = [[AMapReGeocodeSearchRequest alloc] init];
    regeoRequest.location = [AMapGeoPoint locationWithLatitude:lItem.coordinate.latitude longitude:lItem.coordinate.longitude];
    regeoRequest.radius = 10000;
    regeoRequest.requireExtension = YES;
     [self.lManager stopUpdatingLocation];
     //发起逆地理编码
    [_search AMapReGoecodeSearch:regeoRequest];
}
//逆地理编码的回调函数->数据处理并回调相应VC
- (void)onReGeocodeSearchDone:(AMapReGeocodeSearchRequest *)request response:(AMapReGeocodeSearchResponse *)response
{
      //TO DO
}

总结下来定位当前地址的流程:
发起定位->定位回调里拿到坐标->发起逆地理编码-> 逆地理编码回调函数->数据处理并回调相应VC

而对某坐标进行逆地理编码验证则也是通过:
发起逆地理编码-> 逆地理编码回调函数->数据处理并回调相应VC

细心的同学已经发现了:两个不同的功能都会走同一个高德回调函数。这样就有可能造成区分不清对应关系的问题。

  • 项目初期是在LocationManger里用一个全局的BOOL值去区分是来自哪种功能(不高频率切换使用两种功能的话,基本能掩盖这个问题)。
  • 后来QA同事中度暴力测试发现了这个问题,临上线改变方案:发起对某坐标逆地理编码验证功能时全局保存此坐标,并在逆地理回调函数里用此全局坐标作区分。(相当于用坐标代替BOOL值去作区分,准确度大增)。
  • 维持了大概两个版本,QA同事重度暴力测试又发现了这个问题,尽管出现概率不高,但还是在leader的指导下,采用了最后的解决方案。如下:
    在发起定位/对某坐标进行逆地理编码验证时分别给request动态绑定一个bSearchAddress属性
//构造AMapReGeocodeSearchRequest对象,location为必选项,radius为可选项
AMapReGeocodeSearchRequest *regeoRequest = [[AMapReGeocodeSearchRequest alloc] init];
[regeoRequest setValue:@"0" forKey:ReGeocodeSearchRequestKey];//标记为定位模式,回调里用于区分
AMapReGeocodeSearchRequest *regeoRequest = [[AMapReGeocodeSearchRequest alloc] init]; 
[regeoRequest setValue:@"1" forKey:ReGeocodeSearchRequestKey];//标记为地址验证模式,回调里用于区分

这样就能在逆地理回调里进行区分处理:

//实现逆地理编码的回调函数
- (void)onReGeocodeSearchDone:(AMapReGeocodeSearchRequest *)request response:(AMapReGeocodeSearchResponse *)response
{
    if(response.regeocode != nil)
    {
        NSString *bSearchAddress = [request valueForKey:ReGeocodeSearchRequestKey];
        //通过AMapReGeocodeSearchResponse对象处理搜索结果
        if ([bSearchAddress isEqualToString:@"1"]) {
            //地址验证模式
        }else {
            //定位模式
        }
    }
}
这里需要提一下,给对象动态绑定属性需要用到RunTime+Category:
#import "AMapReGeocodeSearchRequest+bSearchAddress.h"
#import <objc/runtime.h>

@implementation AMapReGeocodeSearchRequest (bSearchAddress)
@dynamic bSearchAddress;
static char str_bSearchAddress;

- (void)setBSearchAddress:(NSString *)bSearchAddress
{
    [self willChangeValueForKey:@"bSearchAddress"];
    objc_setAssociatedObject(self, &str_bSearchAddress, bSearchAddress, OBJC_ASSOCIATION_RETAIN);
    [self didChangeValueForKey:@"bSearchAddress"];
}

- (NSString *)bSearchAddress
{
    return objc_getAssociatedObject(self, &str_bSearchAddress);
}
@end

痛点二

同一页面有两处需要用到同一个地址搜索的功能,如何区分的问题。
在刚开始实现这个需求的时候,我还是很天真的在ViewController中用BOOL值去区分这两处的搜索,以期能在LocationManger回调给页面的方法里作区分。这个问题的解决方案应该更丰富一点,我最后是这么解决的:

  • 在第一处直接调用LocationManger中封装好的地址搜索方法:
- (void)startAMapKeyWordsSearchRequest:(AMapPOIKeywordsSearchRequest *)request;//高德地址搜索
  • 对于第二处的处理,这里需要定义一个requestAddressObject,在requestAddressObject中封装LocationManger中对应的高德地址搜索方法,成为LocationManger的代理并实现地址搜索的回调函数。
#import <Foundation/Foundation.h>
#import "LocationManger.h"

@protocol RequestAddressObjectDelegate <NSObject>
- (void)requestKeywordsPOISuccess:(AMapPOISearchResponse *)response;
- (void)requestKeywordsPOIfailed:(NSString *)sError;
@end

@interface requestAddressObject : NSObject
<
LocationManagerDelegate
>
@property (weak, nonatomic) id<RequestAddressObjectDelegate> delegate;
@property (weak, nonatomic) LocationManger *locationManager;
@property (strong, nonatomic) AMapPOIKeywordsSearchRequest *requestKeywordsPlaceList;

- (void)requestPOIKeywordSearch:(AMapPOIKeywordsSearchRequest *)request;
- (void)requestPOIWithKeyword:(NSString *)keyword city:(NSString *)city cityLimit:(BOOL)cityLimit;

@end
#import "requestAddressObject.h"
@implementation requestAddressObject
- (LocationManger *)locationManager{
    if (!_locationManager) {
        _locationManager = [LocationManger getInstanceWithDelegate:self];
    }else {
        if (_locationManager.delegate != self) {
            _locationManager.delegate = self;
        }
    }
    return _locationManager;
}
- (void)requestPOIKeywordSearch:(AMapPOIKeywordsSearchRequest *)request
{
    [self.locationManager startAMapKeyWordsSearchRequest:request];
}
- (void)lmGetPOISearchDelegate:(AMapPOISearchResponse *)response
{
    if (self.delegate && [self.delegate respondsToSelector:@selector(requestKeywordsPOISuccess:)]) {
        [self.delegate requestKeywordsPOISuccess:response];
    }
}
- (void)lmGetLocationFaild:(NSString *)sError
{
    if (self.delegate && [self.delegate respondsToSelector:@selector(requestKeywordsPOIfailed:)]) {
        [self.delegate requestKeywordsPOIfailed:sError];
    }
}
@end
  • 即用requestAddressObject去发起第二处的地址搜索请求,在requestAddressObject中实现LocationManger的回调函数并用requestAddressObjectDelegate定义好的方法回调到VC中进行最后的数据处理和页面展示。这样就达到了区分同一页面两处用到同一地址搜索方法的效果。
//ViewController中的第二处的处理

//懒加载
- (requestAddressObject *)reqAddressObj
{
    if (!_reqAddressObj) {
        _reqAddressObj = [[requestAddressObject alloc] init];
        _reqAddressObj.delegate = self;
    }
    return _reqAddressObj;
}

//第二处地址搜索的调用
[self.reqAddressObj requestPOIKeywordSearch:_requestKeywordsPlaceList];

//LocationManager的回调方法经由RequestAddressObject中转了一次之后回到VC的回调方法
#pragma mark - RequestAddressObjectDelegate
- (void)requestKeywordsPOISuccess:(AMapPOISearchResponse *)response
{
    //第二处地址搜索成功的回调
}

- (void)requestKeywordsPOIfailed:(NSString *)sError
{
    //第二处地址搜索失败的回调
}

输入选址
小结:两个小痛点都是如何对数据进行严格的区分问题。第一个是通过对request进行动态绑定属性来进行区分;第二个是通过用不同的请求方式(VC直接发起请求、间接通过object发起请求)来回避数据区分的问题。两个问题刚好是从不同的角度去解决问题,一个正面交锋;一个迂回战术。方案本无优劣,具体问题具体分析才是王道!
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,875评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,569评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,475评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,459评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,537评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,563评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,580评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,326评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,773评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,086评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,252评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,921评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,566评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,190评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,435评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,129评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,125评论 2 352

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,996评论 25 707
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,086评论 4 62
  • 雨殁空阶 断青丝成乱香销疾风 裹一枝霜残春晖秋月 轮回重楼巅看花零泪 不共楚王一言 箜篌声声 只为镜中笑颜倾杯独步...
    梦饮千樽月阅读 575评论 0 48
  • 看慕芝评: 看标题还以为是撩妹高手 点进来一看 wtf…这还要你说? 周一,告诉我一个让自己打满鸡血的办法。。
    Graceland阅读 197评论 1 2
  • 亲子日记第八天,自从报名参加女儿的六一亲子广场舞我就开始愁,因为从来没跳过,看了视频后觉得不难,可是跳了几下...
    AA稳稳阅读 219评论 0 0