【问题标题】:Crash after dropping pins on map in iOS6在 iOS6 中在地图上放置大头针后崩溃
【发布时间】:2013-05-22 21:27:06
【问题描述】:

我正在尝试学习如何在 iOS 6 中的地图上放置图钉。我有一个可以编译和运行但显然会泄漏内存的代码——但是当我释放(或自动释放)mapData 对象时,我的应用程序崩溃了。错误是

AddressAnnotation 的实例0x1b7ac0 已被释放,而键值观察者仍向其注册。观察信息被泄露,甚至可能被错误地附加到其他对象上。在NSKVODeallocateBreak 上设置断点以在调试器中停止。

之前有一篇关于这个错误的帖子,也与 MapKit 相关:
Setting breakpoint at NSKVODeallocateBreak

但这对我没有帮助:
首先,我并不真正理解答案,但似乎答案与我的问题无关,因为我没有以任何方式设置观察者(我知道,就是这样!)例如,在我的代码中没有任何地方我有台词吗

[addressAnnotation addObserver:self forKeyPath:kSelectedAnnotationObserverKeyPath options:NSKeyValueObservingOptionNew context:@"selectedOrDeselected"];

或任何其他远程类似的东西,这被认为是问题。

话虽如此,我也应该说我不太了解观察者的概念——我当然创建了一个自定义类MapData,它是一个NSObject <MKAnnotation>,我想这也可能是一个问题的根源。但我基本上是一头雾水。

我尝试设置建议的符号断点,但这对我没有帮助:我看到我有一个BAD ACCESS 条件,但这就是我真正理解的全部!

我写的代码是这样的:

- (void) showRecordsOnMap
{
    NSMutableArray *projectMapAnnotationsArray;
    projectMapAnnotationsArray = [[NSMutableArray alloc] init];

    int i = 0;
    for (i = 0; i < [currentProject.recordArray count]; i++)
    {
        Record *record = [[[Record alloc] init]autorelease];
        record = [currentProject.recordArray objectAtIndex:i];

        CLLocationCoordinate2D newCoordinate;
        newCoordinate.latitude = record.latitude;
        newCoordinate.longitude = record.longitude;
        int tag = 0;
        NSString *title;
        title = [[[NSString alloc] init] autorelease];
        title =  [NSString stringWithFormat:@"Record %d",record.record_ID];
        NSString *subtitle;
        subtitle = [[[NSString alloc] init] autorelease];
        subtitle =  [NSString stringWithFormat:@"Record %d",record.record_ID];

        MapData *mapData =[[MapData alloc] initWithCoordinate:newCoordinate withTag:tag withTitle:title withSubtitle:title];

        [projectMapAnnotationsArray addObject:mapData];

        //[mapData release];

    }

    [projectMap addAnnotations:projectMapAnnotationsArray];
    [projectMapAnnotationsArray release];

}

然后是下一个需要的位

- (MKAnnotationView *) mapView:(MKMapView *)mapView
             viewForAnnotation:(MapData *)annotation
{       
    static NSString *record = @"record";

    //the result of the call is being cast (MKPinAnnotationView *) to the correct
    //view class or else the compiler complains
    MKPinAnnotationView *annotationView = (MKPinAnnotationView *)[projectMap
                                                                  dequeueReusableAnnotationViewWithIdentifier:record];
    if(annotationView == nil)
    {
        annotationView = [[[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:record] autorelease];
    }

    //if((annotation).tag == 2) annotationView.pinColor = MKPinAnnotationColorRed;
    //else annotationView.pinColor = MKPinAnnotationColorGreen;

    annotationView.pinColor = MKPinAnnotationColorGreen;
    //pin drops when it first appears
    annotationView.animatesDrop=TRUE;

    //tapping the pin produces a gray box which shows title and subtitle
    annotationView.canShowCallout = YES;

    return annotationView;
}

只要未释放 mapData 对象,此代码就会运行。但显然我需要释放它。作为另一个线索,如果我取消注释

// if((annotation).tag == 2) annotationView.pinColor = MKPinAnnotationColorRed;
// else annotationView.pinColor = MKPinAnnotationColorGreen;

我收到另一个错误:

[MKUserLocation 标记]:无法识别的选择器发送到实例 0x9fcb010 2013-05-22 23:05:13.726 Geo360[1175:c07] *** 由于未捕获的异常 'NSInvalidArgumentException' 终止应用程序,原因:'-[MKUserLocation tag]: 无法识别的选择器发送到实例 0x9fcb010'

但在我看来,第二个错误对我来说是一个更简单的愚蠢,我至少知道如何去寻找。但是错误“AddressAnnotation”让我完全迷失了方向。非常感谢任何帮助!


编辑:

大家好 -- 感谢您抽出宝贵时间提供帮助。我仍然很困惑。附件是 AnnaKarenina 建议的 MapData 对象的代码。 Verbumdei 建议我将数组放在 ViewDidLoad 方法中作为一个强大的属性——我已经玩过了,但我也希望能够用一个可能有更多数据或更少数据的数组来刷新地图图钉,所以在我看来我每次都需要重新制作阵列。也许不是? AnnaKarenina 建议 MapData 中可能存在发布问题,现在我看到它有点怀疑我没有发布标签 - 但另一方面,这样做会产生警告!

再次感谢您的帮助...仍未解决。

MapData.h

#import <Foundation/Foundation.h>
#import <CoreLocation/CoreLocation.h>
#import <MapKit/MapKit.h>

@interface MapData : NSObject <MKAnnotation>
{
    NSString *_title;
    NSString *subtitle;
    NSUInteger tag;
    CLLocationCoordinate2D _coordinate;
}

@property (nonatomic, readonly) CLLocationCoordinate2D coordinate;
@property (nonatomic, copy) NSString *title;
@property (nonatomic, copy) NSString *subtitle;
@property(nonatomic) NSUInteger tag;
// Getters and setters

- (id)initWithCoordinate:(CLLocationCoordinate2D)c withTag:(NSUInteger)t withTitle:(NSString *)tl withSubtitle:(NSString *)s;
@end

MapData.m

#import "MapData.h"

@implementation MapData

@synthesize coordinate;
@synthesize title;
@synthesize subtitle;
@synthesize tag;

-(id)initWithCoordinate:(CLLocationCoordinate2D)c withTag:(NSUInteger)t withTitle:(NSString *)tl withSubtitle:  (NSString *)s
{
    if(self = [super init])
    {
        coordinate = c;
        tag = t;
        title = tl;
        subtitle = s;
    }
    return self;        
}

- (void) dealloc
{
    [title release];
    [subtitle release];
    [super dealloc];
}

@end

【问题讨论】:

  • 为什么要在showRecordsOnMap方法的最后释放projectMapAnnotationsArray?
  • 正如documentation for addAnnotations 所说,“地图视图保留了单个注释对象”,因此可以(并且您应该)发布 projectMapAnnotationsArray。崩溃可能是由于 MapData init 方法中的一些内存管理问题,例如在 mapData 上调用 release 会导致过度释放。发布 MapData init 方法的代码可能会有所帮助。
  • KVO 错误消息可能是与崩溃不同的问题,并且由于坐标无效(请参阅stackoverflow.com/questions/5872547/…)。

标签: objective-c ios6 memory-leaks mapkit


【解决方案1】:

关于 EXC_BAD_ACCESS 的崩溃,这很可能是由于 MapData initWithCoordinate 方法中的这段代码:

title = tl;
subtitle = s;

通过这种方式初始化实例变量,字符串不会被 MapData 对象保留。当您调用 [mapData release] 时,字符串会被释放,然后当地图视图尝试访问注释的标题和副标题时,它会崩溃。

将初始化改为:

title = [tl copy];
subtitle = [s copy];

并取消评论[mapData release];


关于“在键值观察者仍在注册时释放...”警告消息,这可能是由于注释使用了无效坐标(请参阅Warning in Custom Map Annotations iPhone)。对于每个注释,确保纬度在 -90 到 90 之间,经度在 -180 到 180 之间。


关于“[MKUserLocation tag]: unrecognized selector”崩溃,这与上述两个问题无关。发生此错误是因为地图视图为 all 注释调用了 viewForAnnotation 委托方法——而不仅仅是您添加的那些。这意味着它也被称为地图视图本身创建的用户位置蓝点。该用户位置注释的类型为MKUserLocation,而您的自定义注释的类型为MapData。当地图视图为用户位置调用viewForAnnotation 时,该代码会崩溃,因为MKUserLocation 类没有tag 属性。

最简单的处理方法是在 viewForAnnotation 方法的顶部,检查注解是否为 MKUserLocation 类型并为视图返回 nil(它告诉地图视图显示默认视图是用户位置的蓝点)。

另外,不要将viewForAnnotation 方法中annotation 参数的类型声明更改为您的自定义类型(即使它“有效”)。正如所解释的,该方法是为MKUserLocation 以及您的自定义类(或多个类)调用的。将其保留为泛型类型id&lt;MKAnnotation&gt;,这意味着“实现MKAnnotation 协议的对象”。然后,要访问您的自定义类的属性,请将 annotation 强制转换为您的自定义类类型。

所以viewForAnnotation方法的顶部应该是这样的:

- (MKAnnotationView *)mapView:(MKMapView *)mapView 
    viewForAnnotation:(id<MKAnnotation>)annotation
{
    if ([annotation isKindOfClass:[MKUserLocation class]])
        return nil;  //show standard blue dot view for user location

然后更改这些行:

if((annotation).tag == 2) annotationView.pinColor = MKPinAnnotationColorRed;
else annotationView.pinColor = MKPinAnnotationColorGreen;

到这里:

if ([annotation isKindOfClass:[MapData class]])
{
    MapData *mapData = (MapData *)annotation;
    if (mapData.tag == 2)
        annotationView.pinColor = MKPinAnnotationColorRed;
    else
        annotationView.pinColor = MKPinAnnotationColorGreen;
}


还有一些其他的事情(与崩溃或错误无关):

  • 这个模式是错误的:

    Record *record = [[[Record alloc] init]autorelease];
    record = [currentProject.recordArray objectAtIndex:i];
    

    alloc+init+autorelease 是没有意义的,因为变量会立即重新分配给另一个已经分配的对象。相反,只需声明并分配局部变量:

    Record *record = [currentProject.recordArray objectAtIndex:i];
    

    titlesubtitle 的设置方法相同。

  • showRecordsOnMap 中,tag 始终为0。也许您的代码尚未完成,但请确保为每个注释设置不同的值。如果您将所有注释的标签保留为零,则您在viewForAnnotation 中设置基于标签的引脚颜色的代码将无法按预期工作。

  • 在这一行中,您将 title 传递给 title 和 subtitle 参数:

    MapData *mapData =[[MapData alloc] initWithCoordinate:newCoordinate 
        withTag:tag withTitle:title withSubtitle:title];
    
  • viewForAnnotation 中,在将现有注释视图出列后,您应该将重用视图的annotation 属性更新为当前属性(添加else 部分):

    if (annotationView == nil)
    {
        annotationView = [[[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:record] autorelease];
    }
    else
    {
        //We're re-using a view from another annotation
        //that's no longer on screen.
        //Update the view's annotation to the current one...
        annotationView.annotation = annotation;
    }
    

【讨论】:

  • 哇!谢谢,这看起来很有希望!我会试一试并报告!
  • 安娜卡列尼娜,你是对的。代码还没有工作(因为你是对的:我在坐标中有空值,我知道如何修复)。其他错误也没有了,谢谢你的详细解释,现在我也知道为什么了。 Objective C 与 FORTRAN 或 BASIC 非常不同!您的其他 cmets 也很有帮助。我的分数不到 15 分,所以不能投票给你,但我认为这个问题(很好)已经回答了。谢谢!
【解决方案2】:

虽然注释显示在地图中,但您不得释放 MapData 对象。如果要释放 MapData 对象,则需要先从地图中删除注释。

我建议您将 projectMapAnnotationsArray 作为视图控制器的强属性。在viewDidLoad 方法中分配它。然后你可以在视图控制器的dealloc 方法中释放它。

showRecordsOnMap 方法中,您可以只将对象添加到projectMapAnnotationsArray 而无需释放数组本身。

【讨论】:

  • 大家好 -- 感谢您抽出宝贵时间提供帮助。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-02-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-11-10
相关资源
最近更新 更多