将 Parse PFGeoPoints 数组显示为地图注释错误

Display an array of Parse PFGeoPoints as Map Annotations Error

我试图在地图上显示存储在 NSArray 中的对象。在执行获取用户 2 英里范围内位置的方法后,对象从名为 Affiliates 的 Parse.com class 中提取。我已经使用 NSLog 来确认查询正在正确执行,所以我知道提取也正在正确执行。

当我尝试 运行 应用程序时,我在 for(NSDictionary) 部分抛出错误并且应用程序冻结。错误状态 "Thread 1: EXC_BAD_ACCESS (access code 1, address 0x80120)" 在咨询 Google 后,我发现这是某种类型的内存分配问题,但我不知道它为什么会发生或如何修复它。

#import "ViewController.h"
#import "MapAnnotation.h"

@import CoreLocation;

@interface ViewController () <CLLocationManagerDelegate>

@property (nonatomic,strong) NSArray *affiliates;

@end

@implementation ViewController

- (void)viewDidLoad {


    [super viewDidLoad];

    // Ask for authorization to collect location from user
    CLLocationManager * locationManager = [[CLLocationManager alloc] init];
    // Check for iOS 8. Without this guard the code will crash with "unknown selector" on iOS 7.
    if ([locationManager respondsToSelector:@selector(requestWhenInUseAuthorization)]) {

        locationManager.delegate = self;
        locationManager.distanceFilter = kCLDistanceFilterNone;
        locationManager.desiredAccuracy = kCLLocationAccuracyBest;
        [locationManager startUpdatingLocation];
        [locationManager requestWhenInUseAuthorization];
    }

    //Set map options
    self.mapView.showsUserLocation = YES;
    self.mapView.delegate = self;
    self.mapView.scrollEnabled = YES;
    self.mapView.zoomEnabled = YES;
    self.mapView.userTrackingMode = YES;

    // Store the user's location as a Parse PFGeoPoint and call the fetchAffiliatesNearPoint method
    [PFGeoPoint geoPointForCurrentLocationInBackground:^(PFGeoPoint *geoPoint, NSError *error) {
        if (!error) {
            [self fetchAffiliatesNearPoint:geoPoint];
            NSLog(@"Got User Location! %@", geoPoint);
        }
    }];

    /* This is where I am having issues

    for(NSDictionary *affiliates in affiliates) {
        CLLocationCoordinate2D annotationCoordinate = CLLocationCoordinate2DMake([affiliates[@"latitude"] doubleValue], [affiliates[@"longitude"] doubleValue]);

        MapAnnotation *annotation = [[MapAnnotation alloc] init];
        annotation.coordinate = annotationCoordinate;
        annotation.title = affiliates[@"name"];
        annotation.subtitle = affiliates[@"url"];
        [self.mapView addAnnotation:annotation];
    }

*/

}


//Fetch an array of affiliates that are within two miles of the user's current location.
- (void)fetchAffiliatesNearPoint:(PFGeoPoint *)geoPoint
{

    PFQuery *query = [PFQuery queryWithClassName:@"Affiliates"];
    [query whereKey:@"geometry" nearGeoPoint:geoPoint withinMiles:2.0];
    [query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
        if (!error)
        {
            self.affiliates = objects;
            NSLog(@"Nearby Locations %@", _affiliates);
        }
    }];
}


-(void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation
{


    //Zoom map to users current location
    if (!self.initialLocation) {
        self.initialLocation = userLocation.location;
        MKCoordinateRegion mapRegion;
        mapRegion.center = mapView.userLocation.coordinate;
        mapRegion.span.latitudeDelta = .025;
        mapRegion.span.longitudeDelta = .025;

        [mapView setRegion:mapRegion animated: YES];
        }

}
@end

如果要将自定义对象添加到 MapKit 地图,您的自定义对象必须符合 MKAnnotation 协议。只有一个 属性 您需要实现才能使您的对象符合协议:

@property (nonatomic, readonly) CLLocationCoordinate2D coordinate;

其余属性(例如 titlesubtitle)和方法是可选的。

您需要做的就是在 PFGeoPoint 上创建一个类别(假设您正在使用 Objective-C)并为 coordinate 属性 实施 getter。

PFGeoPoint+Annotation.h

@interface PFGeoPoint (Annotation) <MKAnnotation>

@end

PFGeoPoint+Annotation.m

@implementation PFGeoPoint (Annotation)

- (CLLocationCoordinate2D)coordinate {
    return CLLocationCoordinate2DMake(self.latitude, self.longitude);
}

@end

最后,要将其添加到地图,您将使用:

- (void)addAnnotation:(id <MKAnnotation>)annotation;

例如:[mapView addAnnotation:geoPoint];

我也没有使用过 Parse,但有几个明显的问题可能会有所帮助:

  1. 这行没有意义:

    for(NSDictionary *affiliates in affiliates)
    

    这里,in之后的affiliates指的是locally声明的字典变量,not指的是属性 同名变量。由于局部变量没有初始化引用并且指向一些随机内存,所以你得到 EXC_BAD_ACCESS。使用 self.affiliates 明确引用 属性.

    此外,非常 将循环变量命名为与它正在循环的数组相同的名称会令人困惑。您正在遍历 "affiliates"(复数)数组。该数组包含字典,每个字典都是 "affiliate"(单数)。将字典变量命名为 affiliate(单数)会更容易混淆。示例:

    for (NSDictionary *affiliate in self.affiliates)
    

    同时将循环内剩余的引用从 affiliates 更改为 affiliate

  2. 目前,self.affiliates 上的循环是在 geoPointForCurrentLocationInBackground 开始 后立即完成的。 geoPointForCurrentLocationInBackground 似乎是异步的,这意味着 self.affiliates 上的循环将在 geoPointForCurrentLocationInBackground 实际设置 属性 之前执行

    不要在 geoPointForCurrentLocationInBackground 开始后立即在 viewDidLoad 中循环 self.affiliates,而是将循环移动到 findObjectsInBackgroundWithBlock 的完成块内(之后 self.affiliates 已设置)。示例:

    [query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
        if (!error)
        {
            self.affiliates = objects;
            NSLog(@"Nearby Locations %@", _affiliates);
    
            for(NSDictionary *affiliate in self.affiliates) {
                ...
            }
        }
    }];