MKMapView 注释容器持续崩溃:viewForAnnotation
我看过其他关于此的报告,但似乎没有一个适用。我们的应用程序因频繁的(但不能可靠地重现)崩溃而瘫痪,如下所示:
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:
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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
好吧,这才是真正的答案。它来自 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.
我遇到了类似的问题,我尝试了您的解决方案来设置我的
mapView.delegate=nil
但它仅在我第一次重绘地图视图时有效。第二次之后就崩溃了。就我而言,错误发生在这一行。
[CLPlacemark dealloc]
我的“StashMarker”源自 MKPlacemark。
所以我只需更改“StashMarker”的构造函数
从这里
到这个
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
To this
CLPlacemark which is the parent of MKPlacemark and your Annotation class needs an 'addressDictionary' to be released.
(免责声明:我不知道这是否会导致您的问题。)
在评论中,我说:
你问:
实际上,重要的不是一次性全部删除部分,而是对数组索引的依赖。
我不想假设我在一行的索引
i
处获得的注释在其他时间(甚至下一行)仍然存在,即使我的代码没有添加或删除任何注释之间。为了方便起见,地图视图(我认为)只是将注释列表作为数组提供给我们,但在内部它可能会以不同的方式存储列表,并且可能会随机排列内容(但不会更改注释引用)。
我觉得不假设注释数组内容将保留在固定位置会更安全。
因此,要仅删除某些注释,我更喜欢这样:
同样,这可能与您遇到的崩溃无关。
(Disclaimer: I don't know if this is causing your issue.)
In the comments, I said:
and you asked:
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:
Again, this may have nothing to do with the crashes you are experiencing.