-->

Extending CLPlacemark results in EXC BAD ACCESS

2019-02-27 06:08发布

问题:

Though there is a similar question found here it does not provide an answer, at least not for the general problem.

My problem is: Since CoreLocation geocoding is rate limited and the (web-)service I am developing an app for provides its own fallback geocoding service, I want to use this custom geocoding service in case I reach Apple's rate limit. Furthermore, I feel it makes total sense to avoid a custom data type for results returned by this custom REST API and therefore would like to use the data returned to generate CLPlacemarks. However, the documentation states that CLPlacemark properties such as location, locality, administrativeArea etc. are read-only. I therefore created a subclass of CLPlacemark synthesizing the needed properties onto private variables I can access, i.e.:

// interface: (.h)
@interface CustomPlacemark : CLPlacemark
- (nonnull id)initWithLocation: (nonnull CLLocation *)location
                      locality: (nullable NSString *)locality                       
            administrativeArea: (nullable NSString *)adminArea
                       country: (nullable NSString *)country;
@end

// implementation (.m)
@implementation CustomPlacemark
@synthesize location = _location;
@synthesize locality = _locality;
@synthesize country = _country;
@synthesize administrativeArea = _administrativeArea;

- (nonnull id)initWithLocation: (nonnull CLLocation *)location
                          locality: (nullable NSString *)locality
                administrativeArea: (nullable NSString *)adminArea
                           country: (nullable NSString *)country{
    self = [super init];
    if(self){
        _location = location;
        _locality = locality;
        _administrativeArea = adminArea;
        _country = country;
    }
    return self;
}
@end

Testing this code with a unit test which parses data from a JSON file and calls my initWithLocation: locality: administrativeArea: country: method with the data results in a EXC BAD ACCESS (code=1) at the end of the test (at the closing } of the test method) with the placemark variable pointing to nil although a prior NSLog(@"placemark: %@", customPlacemark); outputs the correct values. Furthermore, stepping through the test line by line shows the CustomPlacemark working (i.e. pointing to a properly populated object) until reaching the end of the test. To me this indicates that something with the deallocation of my CustomPlacemark goes wrong - but what exactly?

Any help is greatly appreciated!

回答1:

As a reference to anyone landing here with a similar problem:

After some intensive Google-Fu and deep diving into Apple's sources, it seems as if extending CLPlacemark is not intended.

I was, however, able to implement a workaround based on the tips found here, which basically abuses the fact that MKPlacemark extends CLPlacemark and offers a method to initialise with custom data, namely - (instancetype _Nonnull)initWithCoordinate:(CLLocationCoordinate2D)coordinate addressDictionary:(NSDictionary<NSString *, id> * _Nullable)addressDictionary. Finding the right keys for the addressDictionary to map the desired properties in CLPlacemark might require some trial and error, especially since ABPerson/Addressfunctionality has become deprecated with iOS 9. The keys I found for my purposes are:

@"City" -> CLPlacemark.city
@"State" -> CLPlacemark.administrativeArea
@"Country" -> CLPlacemark.country