LocationManager定位、地址搜索相關(guān)痛點(diǎn)解決(Base on高德)

地圖選址

項(xiàng)目中對(duì)定位、地址搜索等功能采用了LocationManager單例模式,關(guān)于單例的優(yōu)點(diǎn)在此就不贅述了,這里主要想分享一下單例模式下LocationManager的兩個(gè)痛點(diǎn)。

首先簡單貼一下單例的實(shí)現(xiàn):

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;
}

當(dāng)然,更推薦這樣的寫法:

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

接下來,開始分析痛點(diǎn):

痛點(diǎn)一

項(xiàng)目中某頁面需要同時(shí)用到定位當(dāng)前地址+對(duì)某坐標(biāo)進(jìn)行逆地理編碼驗(yàn)證兩個(gè)功能,高德回調(diào)函數(shù)難以區(qū)分。
當(dāng)前地址的邏輯大致如下(非完整代碼):

//發(fā)起定位
[self.lManager startUpdatingLocation];
//定位回調(diào)里拿到坐標(biāo)->發(fā)起逆地理編碼
-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
    ...
    //構(gòu)造AMapReGeocodeSearchRequest對(duì)象,location為必選項(xiàng),radius為可選項(xiàng)
    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];
     //發(fā)起逆地理編碼
    [_search AMapReGoecodeSearch:regeoRequest];
}
//逆地理編碼的回調(diào)函數(shù)->數(shù)據(jù)處理并回調(diào)相應(yīng)VC
- (void)onReGeocodeSearchDone:(AMapReGeocodeSearchRequest *)request response:(AMapReGeocodeSearchResponse *)response
{
      //TO DO
}

總結(jié)下來定位當(dāng)前地址的流程:
發(fā)起定位->定位回調(diào)里拿到坐標(biāo)->發(fā)起逆地理編碼-> 逆地理編碼回調(diào)函數(shù)->數(shù)據(jù)處理并回調(diào)相應(yīng)VC

而對(duì)某坐標(biāo)進(jìn)行逆地理編碼驗(yàn)證則也是通過:
發(fā)起逆地理編碼-> 逆地理編碼回調(diào)函數(shù)->數(shù)據(jù)處理并回調(diào)相應(yīng)VC

細(xì)心的同學(xué)已經(jīng)發(fā)現(xiàn)了:兩個(gè)不同的功能都會(huì)走同一個(gè)高德回調(diào)函數(shù)。這樣就有可能造成區(qū)分不清對(duì)應(yīng)關(guān)系的問題。

  • 項(xiàng)目初期是在LocationManger里用一個(gè)全局的BOOL值去區(qū)分是來自哪種功能(不高頻率切換使用兩種功能的話,基本能掩蓋這個(gè)問題)。
  • 后來QA同事中度暴力測(cè)試發(fā)現(xiàn)了這個(gè)問題,臨上線改變方案:發(fā)起對(duì)某坐標(biāo)逆地理編碼驗(yàn)證功能時(shí)全局保存此坐標(biāo),并在逆地理回調(diào)函數(shù)里用此全局坐標(biāo)作區(qū)分。(相當(dāng)于用坐標(biāo)代替BOOL值去作區(qū)分,準(zhǔn)確度大增)。
  • 維持了大概兩個(gè)版本,QA同事重度暴力測(cè)試又發(fā)現(xiàn)了這個(gè)問題,盡管出現(xiàn)概率不高,但還是在leader的指導(dǎo)下,采用了最后的解決方案。如下:
    在發(fā)起定位/對(duì)某坐標(biāo)進(jìn)行逆地理編碼驗(yàn)證時(shí)分別給request動(dòng)態(tài)綁定一個(gè)bSearchAddress屬性
//構(gòu)造AMapReGeocodeSearchRequest對(duì)象,location為必選項(xiàng),radius為可選項(xiàng)
AMapReGeocodeSearchRequest *regeoRequest = [[AMapReGeocodeSearchRequest alloc] init];
[regeoRequest setValue:@"0" forKey:ReGeocodeSearchRequestKey];//標(biāo)記為定位模式,回調(diào)里用于區(qū)分
AMapReGeocodeSearchRequest *regeoRequest = [[AMapReGeocodeSearchRequest alloc] init]; 
[regeoRequest setValue:@"1" forKey:ReGeocodeSearchRequestKey];//標(biāo)記為地址驗(yàn)證模式,回調(diào)里用于區(qū)分

這樣就能在逆地理回調(diào)里進(jìn)行區(qū)分處理:

//實(shí)現(xiàn)逆地理編碼的回調(diào)函數(shù)
- (void)onReGeocodeSearchDone:(AMapReGeocodeSearchRequest *)request response:(AMapReGeocodeSearchResponse *)response
{
    if(response.regeocode != nil)
    {
        NSString *bSearchAddress = [request valueForKey:ReGeocodeSearchRequestKey];
        //通過AMapReGeocodeSearchResponse對(duì)象處理搜索結(jié)果
        if ([bSearchAddress isEqualToString:@"1"]) {
            //地址驗(yàn)證模式
        }else {
            //定位模式
        }
    }
}
這里需要提一下,給對(duì)象動(dòng)態(tài)綁定屬性需要用到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

痛點(diǎn)二

同一頁面有兩處需要用到同一個(gè)地址搜索的功能,如何區(qū)分的問題。
在剛開始實(shí)現(xiàn)這個(gè)需求的時(shí)候,我還是很天真的在ViewController中用BOOL值去區(qū)分這兩處的搜索,以期能在LocationManger回調(diào)給頁面的方法里作區(qū)分。這個(gè)問題的解決方案應(yīng)該更豐富一點(diǎn),我最后是這么解決的:

  • 在第一處直接調(diào)用LocationManger中封裝好的地址搜索方法:
- (void)startAMapKeyWordsSearchRequest:(AMapPOIKeywordsSearchRequest *)request;//高德地址搜索
  • 對(duì)于第二處的處理,這里需要定義一個(gè)requestAddressObject,在requestAddressObject中封裝LocationManger中對(duì)應(yīng)的高德地址搜索方法,成為LocationManger的代理并實(shí)現(xiàn)地址搜索的回調(diào)函數(shù)。
#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去發(fā)起第二處的地址搜索請(qǐng)求,在requestAddressObject中實(shí)現(xiàn)LocationManger的回調(diào)函數(shù)并用requestAddressObjectDelegate定義好的方法回調(diào)到VC中進(jìn)行最后的數(shù)據(jù)處理和頁面展示。這樣就達(dá)到了區(qū)分同一頁面兩處用到同一地址搜索方法的效果。
//ViewController中的第二處的處理

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

//第二處地址搜索的調(diào)用
[self.reqAddressObj requestPOIKeywordSearch:_requestKeywordsPlaceList];

//LocationManager的回調(diào)方法經(jīng)由RequestAddressObject中轉(zhuǎn)了一次之后回到VC的回調(diào)方法
#pragma mark - RequestAddressObjectDelegate
- (void)requestKeywordsPOISuccess:(AMapPOISearchResponse *)response
{
    //第二處地址搜索成功的回調(diào)
}

- (void)requestKeywordsPOIfailed:(NSString *)sError
{
    //第二處地址搜索失敗的回調(diào)
}

輸入選址
小結(jié):兩個(gè)小痛點(diǎn)都是如何對(duì)數(shù)據(jù)進(jìn)行嚴(yán)格的區(qū)分問題。第一個(gè)是通過對(duì)request進(jìn)行動(dòng)態(tài)綁定屬性來進(jìn)行區(qū)分;第二個(gè)是通過用不同的請(qǐng)求方式(VC直接發(fā)起請(qǐng)求、間接通過object發(fā)起請(qǐng)求)來回避數(shù)據(jù)區(qū)分的問題。兩個(gè)問題剛好是從不同的角度去解決問題,一個(gè)正面交鋒;一個(gè)迂回戰(zhàn)術(shù)。方案本無優(yōu)劣,具體問題具體分析才是王道!
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 173,284評(píng)論 25 708
  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,232評(píng)論 4 61
  • 雨歿空階 斷青絲成亂香銷疾風(fēng) 裹一枝霜?dú)埓簳熐镌?輪回重樓巔看花零淚 不共楚王一言 箜篌聲聲 只為鏡中笑顏傾杯獨(dú)步...
    夢(mèng)飲千樽月閱讀 587評(píng)論 0 48
  • 看慕芝評(píng): 看標(biāo)題還以為是撩妹高手 點(diǎn)進(jìn)來一看 wtf…這還要你說? 周一,告訴我一個(gè)讓自己打滿雞血的辦法。。
    Graceland閱讀 203評(píng)論 1 2
  • 親子日記第八天,自從報(bào)名參加女兒的六一親子廣場(chǎng)舞我就開始愁,因?yàn)閺膩頉]跳過,看了視頻后覺得不難,可是跳了幾下...
    AA穩(wěn)穩(wěn)閱讀 224評(píng)論 0 0