Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Debugging crash in CoreGraphics/MapKit

I am getting an intermittent crash when my application runs on an iPhone. All the crashes are identical and involve MKMapView Overlays (MKCircleViews) in some way.

From a typical iPhone 4s crash report:

Report Header:

Hardware Model:      iPhone4,1
Process:         EL-GPS-01 [1021]
Path: /var/mobile/Applications/61288E15-74B5-45B9-99A9-E0B58C767816/EL-GPS-01.app/EL-GPS-01
Identifier:      EL-GPS-01
Version:         ??? (???)
Code Type:       ARM (Native)
Parent Process:  launchd [1]

Date/Time:       2011-11-22 15:59:41.065 +0000
OS Version:      iPhone OS 5.0.1 (9A405)
Report Version:  104

Exception Type:  EXC_BAD_ACCESS (SIGSEGV)
Exception Codes: KERN_INVALID_ADDRESS at 0x00000000
Crashed Thread:  6

And the crashed thread:

Thread 6 name: Dispatch queue: com.apple.root.default-priority
Thread 6 Crashed:
0 ??? 0000000000 0 + 0
1 CoreGraphics 0x319a87c2 0x31967000 + 268226
2 CoreGraphics 0x3199a9e6 0x31967000 + 211430
3 MapKit 0x37ec3564 0x37e6f000 + 345444
4 MapKit 0x37ec3652 0x37e6f000 + 345682
5 MapKit 0x37ecc0a4 0x37e6f000 + 381092
6 QuartzCore 0x3341be18 0x33410000 + 48664
7 QuartzCore 0x334d77e0 0x33410000 + 817120
8 QuartzCore 0x3346af24 0x33410000 + 372516
9 libdispatch.dylib 0x3797e892 0x3797b000 + 14482
10 libsystem_c.dylib 0x360e31ca 0x360d9000 + 41418
11 libsystem_c.dylib 0x360e30a0 0x360d9000 + 41120

When the application crashes whilst my iPhone is connected to my laptop I get the following in my output panel:

warning: check_safe_call: could not restore current frame
warning: Unable to restore previously selected frame.

The debugger gives me nothing at all, and the Issue navigator shows a crashed thread with nothing on the stack.

There is a very simple project highlighting the problem here:

https://github.com/1ndivisible/MKOverlayBug

[email protected]:1ndivisible/MKOverlayBug.git

I'm not sure how to approach this. Is there any information here that I can use? It seems the crash is originating deep in the framework.

like image 578
Undistraction Avatar asked Nov 25 '11 09:11

Undistraction


2 Answers

Its a known bug. Apple have acknowledged it. There is currently no fix. All you can do is compound your graphics into fewer annotations. I have found <=3 annotations is safe.

like image 21
Undistraction Avatar answered Oct 20 '22 03:10

Undistraction


I think that you are running out of memory or you are running up against a bug in MapKit. But in it's defense it doesn't seem like you are using the overlays and MK views correctly and are overburdening the MKMapView with keeping track of so many potential overlays. Case in point. When the following method you are adding 50 overlay's to the current location:

-(void)addOverlays
{
    CLLocation *currentLocation = self.mapView.userLocation.location;

    for(int i = 0; i < 50; i++)
    {
       MKCircle *circle = [MKCircle circleWithCenterCoordinate:currentLocation.coordinate radius:50];
       [self.mapView addOverlay:circle];
    }
}

over time this can add up to a large number of overlays that the MKMapView needs to keep track of and MKViews that it potentially has to display at once. With your example code and the iPhone simulator's location simulator the simple bike ride route accumulated over 1800 MKCircle and MKCircleViews in the MKMapView.

Here are a couple things to keep in mind:

  1. Don't add so many overlays per location. 1 is sufficient. 50 is overkill.
  2. One MKCircle should be sufficient for each location. Not sure why you are choosing to keep track of and draw so many circles at each location.
  3. Try to be more efficient about handing overlays over to the MapView. Only give it the overlays it needs for the part of the map it is showing. Check out the WWDC2011 video "Session 111 - Visualizing Information Geographically with MapKit" which shows how to optimize this or the HazardMap sample code.

You could do something along these lines:

-(void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
  {
    // Add it to an NSArray that you use to keep track of ALL the overlays
    [self.allOverlays addObject: MKCircle *circle = [MKCircle circleWithCenterCoordinate:currentLocation.coordinate radius:50]];

     // Now add it to the mapView if it is in the current region

     // code to check to see if currentLocation is within the current map region

     if (overlayIsInMapRegion){
         [mapView addOverlay: circle];
     } 
}

Then whenever the region changes, calculate the overlays that are needed when the MapView calls the delegate method:

- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated

get the region from the mapView and create an array of overlays that are located in that region. Something along the lines of:

- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated{
    // Create a method that takes the region and calculates which overlays it contains
    NSArray *newOverlays=[self overlaysForRegion: mapView.region fromAllOverlays: self.allOverlays];
    if ([newOverlays count]>0){
         [mapView removeOverlays];
         [mapView addOverlays: newOverlays];
    }

}

Hope this helps. Good luck!

Tim

like image 166
timthetoolman Avatar answered Oct 20 '22 03:10

timthetoolman