iOS 에서 아 날로 그 포 지 셔 닝 기능 을 실현 하 는 예시 코드

머리말
App 에서 점점 더 많은 기능 이 사용자 의 실제 위치 에 의존 하고 있다.예 를 들 어 사용자 의 위 치 를 바탕 으로 추천 데 이 터 를 제공 하고 포 지 셔 닝 을 바탕 으로 특정한 기능 이 사용 가능 한 지 판단 하지만 개발 디 버 깅 에서 XCode 는 사용자 정의 아 날로 그 포 지 셔 닝 기능 을 제공 하지 않 기 때문에 본 고의 주요 목적 은 현실 적 으로 디 버 깅 과정 에서 수시로 아 날로 그 포 지 셔 닝 을 할 수 있 는 기능 이다.
사고의 방향
저 희 는 iOS 의 app 개발 에서 보통 CLLocationManager 를 사용 하여 사용자 의 현재 위 치 를 얻 습 니 다.물론 MK MapView 의 showUserLocation 를 사용 하여 사용자 의 위 치 를 얻 을 수 있 기 때문에 저 희 는 이 두 가지 상황 에 대해 분석 합 니 다.
CLLocationManager
CLLocationManager 를 사용 하여 포 지 셔 닝 을 가 져 올 때 CLLocationManager Delegate 중-(void)locationManager:(CLLocationManager*)manager  didUpdateLocations:(NSArray)locations 의 리 셋 으로 포 지 셔 닝 을 가 져 옵 니 다.우 리 는 시스템 에서 이 방법 을 업무 코드 에 전달 하 는 중간 에 일부 코드 를 삽입 하여 locations 인 자 를 수정 해 야 합 니 다.원래 의 논 리 는 시스템 리 셋->업무 코드 였 는데,지금 은 시스템 리 셋->아 날로 그 포 지 셔 닝 모듈->업무 코드 로 바 뀌 어 무 침입 식 아 날로 그 포 지 셔 닝 기능 을 실현 하 였 다.이 논 리 를 실현 하기 위해 서 는 다음 과 같은 몇 가지 사고 가 있 을 수 있다.
1、 Runtime swizzle
비 즈 니스 코드 는-(void)locationManager:(CLLocationManager*)manager 에 따라  didUpdateLocations:(NSArray*)locations 방법 으로 리 셋 을 받 아들 일 수 있 기 때문에 Runtime swizzle 이라는 방법 으로 아 날로 그 포 지 셔 닝 기능 을 실현 할 수 있 습 니 다.그러나 우리 중간 구성 요 소 는 업무 코드 에서 구체 적 으로 어떤 종류 인지 모 르 기 때문에 runtime swizzle 의 어떤 종 류 를 구체 적 으로 지정 할 수 없 기 때문에 모든 종 류 를 옮 겨 다 닐 수 밖 에 없습니다.현재 클래스 의 방법 목록 에 locationManager:didUpdateLocations 가 있 는 지 판단 합 니 다.이 방법 이 존재 하면 swizzle 입 니 다.
  • 장점:이해 하기 쉽다.
  • 단점:모든 클래스 와 클래스 를 옮 겨 다 니 는 방법 목록 이 필요 합 니 다.
  • 2.중간 대리 대상
    이러한 사 고 는 Swizzle 이 CLLocation Manager 의 setDelegate:방법 입 니 다.setDelegate 를 호출 할 때 실제 delegate object 를 저장 한 다음 에 우리 가 정의 하 는 중간 대리 류 swizzle delegate 대상 을 CLLocation Manager 의 delegate 로 설정 합 니 다.그러면 시스템 이 CLLocation Manager Delegate 를 되 돌 릴 때 중간 대리 류 swizzle delegate 로 되 돌 립 니 다.swizzle delegate 에서 사건 을 실제 delegate object 로 전달 합 니 다.
  • 장점:첫 번 째 방법 에 비해 클래스 와 클래스 를 옮 겨 다 니 는 방법 목록 이 필요 없습니다.swizzle CLLocationManager 의 setDelegate:방법 만 있 으 면 됩 니 다.
  • 단점:중간 대리 류 swizzle delegate 에서 모든 CLLocation Manager Delegate 방법 을 실현 해 야 합 니 다.나중에 대리 방법 을 추가 하면 이 종 류 를 수정 해 야 합 니 다.
  • 3.NSProxy 를 이용 하여 중간 대리 대상 실현
    Objective-C 에는 2 개의 기본 클래스 가 있 는데 자주 사용 하 는 것 은 NSObject 이 고 다른 하 나 는 NSProxy 입 니 다.NSProxy 는 주로 메시지 전송 처리 에 사용 되 기 때문에 NSProxy 를 사용 하면 우리 가 더 잘 처리 할 수 있 는 방법 2 의 단점 입 니 다.
    3.1 새로운 종류의 MockLocationProxy 를 만 들 고 NSProxy 에 통합 합 니 다.
    
    // MockLocationProxy.h
    #import <CoreLocation/CoreLocation.h>
    
    @interface MockLocationProxy : NSProxy
    
    @property (nonatomic, weak, readonly, nullable) id <CLLocationManagerDelegate> target;
    
    - (instancetype)initWithTarget:(id <CLLocationManagerDelegate>)target;
    
    @end
    
    
    
    // MockLocationProxy.m
    #import "MockLocationProxy.h"
    
    @implementation MockLocationProxy
    
    - (instancetype)initWithTarget:(id<CLLocationManagerDelegate>)target {
      _target = target;
      return self;
    }
    
    @end
    
    이어서 메시지 전송의 논 리 를 처리 합 니 다.우선 우리 가 원 하 는 효과 가 무엇 인지 알 아야 합 니 다.시스템 은 MockLocationProxy 에 리 셋 되 고,MockLocationProxy 는 locationManager:didUpdateLocations:만 처리 하 며,다른 정 보 는 여전히 원래 target 에 전 달 됩 니 다.
    그래서 저 희 는 MockLocationProxy.m 에 다음 과 같은 방법 을 추가 합 니 다.
    
    // MockLocationProxy.m
    @implementation MockLocationProxy
    
    - (instancetype)initWithTarget:(id<CLLocationManagerDelegate>)target {
      _target = target;
      return self;
    }
    
    - (BOOL)respondsToSelector:(SEL)aSelector {
      if (aSelector == @selector(locationManager:didUpdateLocations:)) {
        return YES;
      }
      return [self.target respondsToSelector:aSelector];
    }
    
    - (void)forwardInvocation:(NSInvocation *)invocation {
      SEL sel = invocation.selector;
      if ([self.target respondsToSelector:sel]) {
        [invocation invokeWithTarget:self.target];
      }
    }
    
    - (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
      return [self.target methodSignatureForSelector:sel];
    }
    
    #pragma mark - CLLocationManagerDelegate
    - (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations {
      if ([self.target respondsToSelector:_cmd]) {
        //       
        CLLocation *mockLocation = [[CLLocation alloc] initWithLatitude:39.908722 longitude:116.397499];
        locations = @[mockLocation];
        [self.target locationManager:manager didUpdateLocations:locations];
      }
    }
    @end
    
    
    MockLocation Proxy 에 메 시 지 를 보 낼 때 현재 방법 이 locationManager:didUpdateLocations:인지 판단 합 니 다.그렇다면 MockLocation Proxy 응답 이벤트 입 니 다.그렇지 않 으 면 원래 target 에 직접 전 달 됩 니 다.아 날로 그 포 지 셔 닝 을 수시로 처리 할 수 있 습 니 다.아 날로 그 포 지 셔 닝 코드 를 처리 하기 만 하면 언제든지 포 지 셔 닝 을 수정 할 수 있다.
    One more.
    상기 방법 은 포 지 셔 닝 을 모 의 할 수 있 지만 아 날로 그 값 을 수정 할 때마다 다시 build 해 야 합 니 다.실행 할 때 이 값 을 수시로 수정 할 방법 이 있 습 니까?
    LLDebugTool
    물론 입 니 다.프로젝트 에 LLDebugTool 을 통합 하고 그 중의 Location 모듈 을 호출 하 십시오.LLDebugTool 은 이 아 날로 그 값 을 수시로 수정 할 수 있 는 UI 를 제공 합 니 다.디 버 깅 할 때 수시로 아 날로 그 포 지 셔 닝 을 할 수 있 습 니 다.LLDebugTool 은 다른 기능 을 많이 제공 합 니 다.만약 에 아 날로 그 포 지 셔 닝 기능 만 필요 하 다 면,LLDebugTool/Location 라 는 subspec 만 통합 하면 됩 니 다.
    후기
    앞에서 말 했 듯 이 포 지 셔 닝 은 CLLocation Manager 를 제외 하고 MK MapView 의 show UserLocation 도 포 지 셔 닝 정 보 를 얻 을 수 있 습 니 다.그러면 이 문 제 를 어떻게 해결 합 니까?너 는LLDebugTool/Location에서 답 을 볼 수 있다.
    이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.

    좋은 웹페이지 즐겨찾기