MKMapView 注释容器持续崩溃:viewForAnnotation

发布于 2024-12-15 18:42:20 字数 3776 浏览 0 评论 0原文

我看过其他关于此的报告,但似乎没有一个适用。我们的应用程序因频繁的(但不能可靠地重现)崩溃而瘫痪,如下所示:

Thread 0 name:  Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
0   libobjc.A.dylib                 0x333a6c98 objc_msgSend + 16
1   MapKit                          0x30be52fc -[MKMapView annotationContainer:viewForAnnotation:] + 36
2   MapKit                          0x30be4f8e -[MKAnnotationContainerView _addViewForAnnotation:] + 270
3   MapKit                          0x30c0f164 -[MKAnnotationContainerView addViewForManagedAnnotation:notifyDelegate:] + 12
4   MapKit                          0x30c0b874 -[MKMapView(UserPositioningInternal) _runPositioningChange] + 1036
5   MapKit                          0x30c09a86 -[MKMapView(UserPositioningInternal) _startPositioningChange:] + 22
6   MapKit                          0x30c0d04a -[MKMapView(UserPositioningInternal) locationManagerUpdatedLocation:] + 578
7   CoreFoundation                  0x360bcefc -[NSObject(NSObject) performSelector:withObject:] + 16
8   CoreFoundation                  0x360fa2f2 -[NSArray makeObjectsPerformSelector:withObject:] + 394
9   MapKit                          0x30bfc802 -[MKLocationManager _reportLocationStatus:] + 34
10  MapKit                          0x30bfdd6c -[MKLocationManager _reportLocationSuccess] + 36
11  MapKit                          0x30bfd9c6 -[MKLocationManager locationManager:didUpdateToLocation:fromLocation:] + 674

网络上出现了大量有关此问题的报告,但许多似乎都没有得到解决。我没有做任何疯狂的事情,只是在地图上显示用户位置和一个标记。我按照我能找到的示例进行操作,并且查看了代码中的错误,但找不到任何错误。

以下是我的 MapView 委托处理 viewForAnnotation 的方式:

- (MKAnnotationView*)mapView:(MKMapView*)theMapView viewForAnnotation:(id <MKAnnotation>)annotation
{
    // If it's the user location, just return nil.
    if([annotation isKindOfClass:[MKUserLocation class]])
    {
        return nil;     // Use default system user-location view.
    }
    else
    {
        // Try to dequeue an existing pin view first.
        MKPinAnnotationView* pinView = (MKPinAnnotationView*)[mapView dequeueReusableAnnotationViewWithIdentifier:@"stashMarkerID"];
        if(!pinView)
        {
            // If an existing pin view was not available, create one.
            pinView = [[[MKPinAnnotationView alloc] initWithAnnotation:annotation
                                                       reuseIdentifier:@"stashMarkerID"] autorelease];
            pinView.pinColor = MKPinAnnotationColorPurple;
            pinView.animatesDrop = YES;
            pinView.canShowCallout = NO;
        }
        else
        {
            pinView.annotation = annotation;
        }

        return pinView;
    }
}

崩溃似乎与用户位置的更改相关,但一旦添加了用户位置标记,我就不会再打扰它。当我需要刷新另一个地图标记时,我会在删除另一个地图标记时跳过用户标记:

// Add the marker to the map.
// Remove old one(s) first.
int i = 0;
while(i < [mapView.annotations count])
{
    if ([[mapView.annotations objectAtIndex:i] isKindOfClass:[StashMarker class]])
    {
        [mapView removeAnnotation:[mapView.annotations objectAtIndex:i]];
    }
    else
    {
        i++;
    }
}

委托是整个屏幕的控制器,因此它不会被释放;崩溃发生在屏幕打开时。这并不重要,但地图显示如下所示:

在此处输入图像描述

任何猜测或见解将不胜感激!这是在 iOS 5.0.1 上。

更新:我们发现当 MKMapView 不应该存在时就会发生此崩溃。包含它的视图早已被弹出。我想知道我们是否遇到了此处报告的问题:MKMapView crashes app when view controller popped

更新 2:这是本质上相同的事情的另一份报告:为什么我在 MKMapView 之后崩溃如果我不再使用它,它会被释放吗?

对于 Cocoa 来说,这似乎异常混乱,在我们期望 MapView 被使用之后,必须将 MKMapView 的委托设置为 nil解除分配。按照这个速度,为什么我们不必对所有需要委托的控件都这样做呢?

I've looked at other reports on this, and none seems to apply. Our app is being crippled by frequent (but not reliably reproducible) crashes like this:

Thread 0 name:  Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
0   libobjc.A.dylib                 0x333a6c98 objc_msgSend + 16
1   MapKit                          0x30be52fc -[MKMapView annotationContainer:viewForAnnotation:] + 36
2   MapKit                          0x30be4f8e -[MKAnnotationContainerView _addViewForAnnotation:] + 270
3   MapKit                          0x30c0f164 -[MKAnnotationContainerView addViewForManagedAnnotation:notifyDelegate:] + 12
4   MapKit                          0x30c0b874 -[MKMapView(UserPositioningInternal) _runPositioningChange] + 1036
5   MapKit                          0x30c09a86 -[MKMapView(UserPositioningInternal) _startPositioningChange:] + 22
6   MapKit                          0x30c0d04a -[MKMapView(UserPositioningInternal) locationManagerUpdatedLocation:] + 578
7   CoreFoundation                  0x360bcefc -[NSObject(NSObject) performSelector:withObject:] + 16
8   CoreFoundation                  0x360fa2f2 -[NSArray makeObjectsPerformSelector:withObject:] + 394
9   MapKit                          0x30bfc802 -[MKLocationManager _reportLocationStatus:] + 34
10  MapKit                          0x30bfdd6c -[MKLocationManager _reportLocationSuccess] + 36
11  MapKit                          0x30bfd9c6 -[MKLocationManager locationManager:didUpdateToLocation:fromLocation:] + 674

Reports of this turn up a lot on the Web, but many seem to go unsolved. I'm not doing anything crazy, just showing the user position and one marker on the map. I followed the examples I could find, and I've looked at the code for goofs but can't find any.

Here's how my MapView delegate handles viewForAnnotation:

- (MKAnnotationView*)mapView:(MKMapView*)theMapView viewForAnnotation:(id <MKAnnotation>)annotation
{
    // If it's the user location, just return nil.
    if([annotation isKindOfClass:[MKUserLocation class]])
    {
        return nil;     // Use default system user-location view.
    }
    else
    {
        // Try to dequeue an existing pin view first.
        MKPinAnnotationView* pinView = (MKPinAnnotationView*)[mapView dequeueReusableAnnotationViewWithIdentifier:@"stashMarkerID"];
        if(!pinView)
        {
            // If an existing pin view was not available, create one.
            pinView = [[[MKPinAnnotationView alloc] initWithAnnotation:annotation
                                                       reuseIdentifier:@"stashMarkerID"] autorelease];
            pinView.pinColor = MKPinAnnotationColorPurple;
            pinView.animatesDrop = YES;
            pinView.canShowCallout = NO;
        }
        else
        {
            pinView.annotation = annotation;
        }

        return pinView;
    }
}

The crash appears to be associated with a change in the user's location, but once the user-location marker is added, I don't mess with it. When I need to refresh the one other map marker, I skip the user marker when removing the other one:

// Add the marker to the map.
// Remove old one(s) first.
int i = 0;
while(i < [mapView.annotations count])
{
    if ([[mapView.annotations objectAtIndex:i] isKindOfClass:[StashMarker class]])
    {
        [mapView removeAnnotation:[mapView.annotations objectAtIndex:i]];
    }
    else
    {
        i++;
    }
}

The delegate is the controller for the whole screen, so it's not being deallocated; the crash occurs while the screen is up. Not that it matters, but the map display looks like this:

enter image description here

Any guesses or insight would be much appreciated! This is on iOS 5.0.1.

UPDATE: We've found that this crash is occurring when no MKMapView should even be in existence. The view containing it has long since been popped. I wonder if we're experiencing the problem reported here: MKMapView crashes app when view controller popped

UPDATE 2: Here's another report of essentially the same thing: Why am I crashing after MKMapView is freed if I'm no longer using it?

This seems unusually messy for Cocoa, having to set the MKMapView's delegate to nil after we'd expect the MapView to be deallocated. At that rate, why don't we have to do this for all controls that take a delegate?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

看透却不说透 2024-12-22 18:42:20

好吧,这才是真正的答案。它来自 Apple 文档,但 MKMapView 中缺少它。它只能在其委托协议的文档中找到:

“在释放已设置委托的 MKMapView 对象之前,请记住将该对象的委托属性设置为 nil。您可以执行此操作的一个地方是在您处置的 dealloc 方法中地图视图。”

注意:这也适用于 UIWebView。

我在委托的 dealloc 方法中将 MapView 的委托指针设置为 nil,我们的崩溃似乎已被消除。

OK, this is the real answer. It's from the Apple doc, but it's missing from MKMapView. It's only found under the documentation for its delegate protocol:

"Before releasing an MKMapView object for which you have set a delegate, remember to set that object’s delegate property to nil. One place you can do this is in the dealloc method where you dispose of the map view."

NOTE: This also applies to UIWebView.

I set the MapView's delegate pointer to nil in the delegate's dealloc method, and our crashes seem to have been eliminated.

维持三分热 2024-12-22 18:42:20

我遇到了类似的问题,我尝试了您的解决方案来设置我的 mapView.delegate=nil 但它仅在我第一次重绘地图视图时有效。第二次之后就崩溃了。
就我而言,错误发生在这一行。
[CLPlacemark dealloc]

我的“StashMarker”源自 MKPlacemark。
所以我只需更改“StashMarker”的构造函数
从这里

self = [super init];

到这个

self = [super initWithCoordinate:your_coord addressDictionary:[NSDictionary dictionary]];

CLPlacemark,它是 MKPlacemark 的父级,并且您的 Annotation 类需要释放一个“addressDictionary”。

I had the similar problem, and I ve tried your solution to set my mapView.delegate=nil but it works only for the 1st time when I redraw a mapview. after the second time it crashes.
In my case error happens at this line.
[CLPlacemark dealloc]

And my 'StashMarker' is derived from MKPlacemark.
So I just change my constructor of 'StashMarker'
From this

self = [super init];

To this

self = [super initWithCoordinate:your_coord addressDictionary:[NSDictionary dictionary]];

CLPlacemark which is the parent of MKPlacemark and your Annotation class needs an 'addressDictionary' to be released.

神经暖 2024-12-22 18:42:20

(免责声明:我不知道这是否会导致您的问题。)

在评论中,我说:

我会通过将要删除的注释添加到另一个注释来删除注释
数组,然后调用removeAnnotations(复数)而不是删除
就地并使用特定索引来引用注释。

你问:

您希望收集注释的任何特定原因
一次性删除并执行所有操作?

实际上,重要的不是一次性全部删除部分,而是对数组索引的依赖。

我不想假设我在一行的索引 i 处获得的注释在其他时间(甚至下一行)仍然存在,即使我的代码没有添加或删除任何注释之间。

为了方便起见,地图视图(我认为)只是将注释列表作为数组提供给我们,但在内部它可能会以不同的方式存储列表,并且可能会随机排列内容(但不会更改注释引用)。

我觉得不假设注释数组内容将保留在固定位置会更安全。

因此,要仅删除某些注释,我更喜欢这样:

NSMutableArray *annotationsToRemove = [NSMutableArray array];

for (id<MKAnnotation> ann in mapView.annotations) 
{
    if ([ann isKindOfClass:[StashMarker class]])
    {
        [annotationsToRemove addObject:ann];
    }
}

[mapView removeAnnotations:annotationsToRemove];

同样,这可能与您遇到的崩溃无关。

(Disclaimer: I don't know if this is causing your issue.)

In the comments, I said:

I would remove the annotations by adding the ones to remove to another
array and then call removeAnnotations (plural) instead of removing
in-place and using the specific index to refer to an annotation.

and you asked:

Any particular reason you'd prefer to collect the annotations to
remove and do them all at once?

Actually, it's not the removing-all-at-once part that's important but the reliance on the array index.

I'd prefer not to assume that the annotation I get at index i on one line will still be there at another time (even the next line) even if my code doesn't add or remove any annotations in between.

The map view (I think) just supplies the annotation list as an array to us for convenience but internally it may be storing the list differently and may shuffle the contents around (but not change the annotation references).

I feel it's safer not to assume that the annotations array contents will remain in fixed positions.

So to remove only certain annotations, I'd prefer this:

NSMutableArray *annotationsToRemove = [NSMutableArray array];

for (id<MKAnnotation> ann in mapView.annotations) 
{
    if ([ann isKindOfClass:[StashMarker class]])
    {
        [annotationsToRemove addObject:ann];
    }
}

[mapView removeAnnotations:annotationsToRemove];

Again, this may have nothing to do with the crashes you are experiencing.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文