views:

2810

answers:

1

So, for the past few days I have been struggling to understand how to implement a simple MKMapView with some custom annotations without crashing my application in the process. Unfortunately I have been unable to determine what I'm doing wrong and am becoming increasingly frustrated in the process.

What I'm trying to accomplish should be relatively simple. I'm trying to create a new object w/ a location associated with it. To do this I have a view controller for creating the object. I want the user to be able to cancel out of the view controller at any time if they so desire, but in order to save the object they must first provide a location and a name for it. The name will be taken via a UITextField while the location will be obtained via the MKMapView.

So here is what happens... Whenever I open up the New Object View Controller, it updates the location and whatnot. If I try to click cancel, it crashes. In an attempt to simplify the problem I removed the code for updating the location and moving the annotation pin, so you won't see that below.

Here is some of the code I'm using in addition to an example of the stack trace I encounter after the crash. Any help you can offer would be greatly appreciated. Thanks!

Before I show you my code, here is the stack trace I end up with:

Stack Trace After Crash

#1 0x30c4c8b8 in -[UIImageView stopAnimating]
#2 0x30c4c810 in -[UIImageView dealloc]
#3 0x32d86640 in -[NSObject release]
#4 0x32d198ac in -[MKAnnotationView dealloc]
#5 0x32d86640 in -[NSObject release]
#6 0x30bfab34 in -[UIView(Hierarchy) removeFromSuperview]
#7 0x30c4ca24 in -[UIView dealloc]
#8 0x32ce881c in -[MKOverlayView dealloc]
#9 0x32d86640 in -[NSObject release]
#10 0x30bfab34 in -[UIView(Hierarchy) removeFromSuperview]
#11 0x30c4ca24 in -[UIView dealloc]
#12 0x30cbb878 in -[UIScrollView dealloc]
#13 0x32d179c4 in -[MKScrollView dealloc]
#14 0x32d86640 in -[NSObject release]
#15 0x30bfab34 in -[UIView(Hierarchy) removeFromSuperview]
#16 0x30cbb4a8 in -[UIScrollView removeFromSuperview]
#17 0x30c4ca24 in -[UIView dealloc]
#18 0x32d86640 in -[NSObject release]
#19 0x30bfab34 in -[UIView(Hierarchy) removeFromSuperview]
#20 0x30c4ca24 in -[UIView dealloc]
#21 0x32cc579c in -[MKMapView dealloc]
#22 0x32d86640 in -[NSObject release]
#23 0x30bfab34 in -[UIView(Hierarchy) removeFromSuperview]
#24 0x30c4ca24 in -[UIView dealloc]
#25 0x32d86640 in -[NSObject release]
#26 0x30bfab34 in -[UIView(Hierarchy) removeFromSuperview]
#27 0x30c4ca24 in -[UIView dealloc]
#28 0x32d86640 in -[NSObject release]
#29 0x30bfab34 in -[UIView(Hierarchy) removeFromSuperview]
#30 0x30c4ca24 in -[UIView dealloc]
#31 0x32d86640 in -[NSObject release]
#32 0x30bfab34 in -[UIView(Hierarchy) removeFromSuperview]
#33 0x30c4ca24 in -[UIView dealloc]
#34 0x32d86640 in -[NSObject release]
#35 0x33f70996 in NSPopAutoreleasePool
#36 0x33e99104 in run_animation_callbacks
#37 0x33e98e6c in CA::timer_callback
#38 0x32da44c2 in CFRunLoopRunSpecific
#39 0x32da3c1e in CFRunLoopRunInMode
#40 0x31bb9374 in GSEventRunModal
#41 0x30bf3c30 in -[UIApplication _run]
#42 0x30bf2230 in UIApplicationMain
#43 0x00002450 in main at main.m:14

CustomAnnotation.h:

@interface CustomAnnotation : NSObject <MKAnnotation, 
          MKReverseGeocoderDelegate>
{
 @private
 MKReverseGeocoder*   _reverseGeocoder;
 MKPlacemark*    _placemark;

 @public
 CLLocationCoordinate2D  _coordinate;
 NSString*     _title;
}

//Note: Property for CLLocationCoordinate2D coordinate is declared in MKAnnotation
@property (nonatomic, retain) NSString*      title;
@property (nonatomic, retain) MKPlacemark*   placemark;

-(id) initWithCoordinate:(CLLocationCoordinate2D)coordinate
       title:(NSString*)title;

-(void) setCoordinate:(CLLocationCoordinate2D)coordinate;

@end

CustomAnnotation.m

@implementation CustomAnnotation

@synthesize coordinate      = _coordinate; // property declared in MKAnnotation.h
@synthesize title   = _title;
@synthesize placemark  = _placemark;

-(id) initWithCoordinate:(CLLocationCoordinate2D)coordinate 
       title:(NSString*)title
{
 if(self = [super init])
 {
  _title      = [title retain];
  [self setCoordinate:coordinate];
  _placemark = nil;
 }
 return self;
}

#pragma mark -
#pragma mark MKAnnotationView Notification

- (void)notifyCalloutInfo:(MKPlacemark *)newPlacemark {
 [self willChangeValueForKey:@"subtitle"]; // Workaround for SDK 3.0, otherwise callout info won't update.
 self.placemark = newPlacemark;
 [self didChangeValueForKey:@"subtitle"]; // Workaround for SDK 3.0, otherwise callout info won't update.

 [[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:@"MKAnnotationCalloutInfoDidChangeNotification" object:self]];
}

#pragma mark
#pragma mark -
#pragma mark Reverse Geocoder Reset Procedure

- (void)resetReverseGeocoder
{
 if(_reverseGeocoder != nil)
 {
  //If the reverse geocoder already exists, check to make sure it isn't querying. Cancel query if it is.
  if([_reverseGeocoder isQuerying])
  {
   [_reverseGeocoder cancel];
  }

  //Before releasing the reverse geocoder, set it's delegate to nil just to be safe
  [_reverseGeocoder setDelegate:nil];

  //Release the current reverse geocoder
  [_reverseGeocoder release];
  _reverseGeocoder = nil;
 }
}

#pragma mark
#pragma mark -
#pragma mark Set Coordinate Procedure

- (void)setCoordinate:(CLLocationCoordinate2D)coordinate {
 _coordinate = coordinate;

 //We only want to be reverse geocoding one location at a time, so make sure we've reset the reverse geocoder before starting
 [self resetReverseGeocoder];

 //Create a new reverse geocoder to find the location for the given coordinate, and start the query
 _reverseGeocoder = [[MKReverseGeocoder alloc] initWithCoordinate:_coordinate];
 [_reverseGeocoder setDelegate:self];
 [_reverseGeocoder start];
}

#pragma mark
#pragma mark -
#pragma mark MKAnnotation Delegate Procedure Implementations

- (NSString *)subtitle
{
 NSString* subtitle = nil;

if (_placemark) 
 {
   subtitle = [NSString stringWithString:[[_placemark.addressDictionary objectForKey:@"FormattedAddressLines"] objectAtIndex:1]];
  } 
  else 
  {
   subtitle = [NSString stringWithFormat:@"%lf, %lf", _coordinate.latitude, _coordinate.longitude];
  }

 return subtitle;
}

#pragma mark -
#pragma mark MKReverseGeocoderDelegate methods

- (void)reverseGeocoder:(MKReverseGeocoder *)geocoder didFindPlacemark:(MKPlacemark *)newPlacemark {

 if(geocoder != _reverseGeocoder)
 {
  NSLog(@"WARNING:::: MORE THAN ONE REVERSE GEOCODER!!!");
  NSLog(@"_reverseGeocoder = %@",[_reverseGeocoder description]);
  NSLog(@"geocoder = %@",[geocoder description]);
 }

 [self notifyCalloutInfo:newPlacemark];

 [self resetReverseGeocoder];
}

- (void)reverseGeocoder:(MKReverseGeocoder *)geocoder didFailWithError:(NSError *)error {

 if(geocoder != _reverseGeocoder)
 {
  NSLog(@"WARNING:::: MORE THAN ONE REVERSE GEOCODER!!!");
  NSLog(@"_reverseGeocoder = %@",[_reverseGeocoder description]);
  NSLog(@"geocoder = %@",[geocoder description]);
 }

 [self notifyCalloutInfo:nil];

 [self resetReverseGeocoder];
}


#pragma mark -
#pragma mark Memory Management

- (void)dealloc {

 [self resetReverseGeocoder];

 [_title   release], _title = nil;
 [_placemark  release], _placemark = nil;

 [super dealloc];
}

@end

Second, I have created a Custom Annotation View

CustomAnnotationView.h

@interface CustomAnnotationView : MKAnnotationView {

}

@end

CustomAnnotationView.m

@implementation CustomAnnotationView

- (id)initWithAnnotation:(id <MKAnnotation>)annotation reuseIdentifier:(NSString *)reuseIdentifier
{
 self = [super initWithAnnotation:annotation reuseIdentifier:reuseIdentifier];

 UIGraphicsBeginImageContext(CGSizeMake(30,30));

        //Note: [UIImage drawInRect:radius:contentMode:] is a Three20 Procedure
 [[UIImage imageNamed:@"annoationIcon.png"] drawInRect:CGRectMake(0,0,30,30) radius:6.0f contentMode:UIViewContentModeScaleAspectFill];

 UIImage *iconImage = UIGraphicsGetImageFromCurrentImageContext();

 //pop the context to get back to the default
 UIGraphicsEndImageContext();

 UIImageView *leftIconView = [[UIImageView alloc] initWithImage:iconImage];
 self.leftCalloutAccessoryView = leftIconView;
 [leftIconView release];

 [iconImage release];

 return self;
}

@end

And, Last But Not Least, the relevant parts of My New Object View Controller:

NewObjectViewController.m

@implementation NewObjectViewController

@synthesize managedObjectContext;

@synthesize object;

@synthesize objectMapView;

#pragma mark
#pragma mark -
#pragma mark Initialization

-(id) initWithManagedObjectContext:(NSManagedObjectContext*)context{
 self = [super init];
 if (self != nil) {

  [self setManagedObjectContext:context]; 

  Object *newObject = [NSEntityDescription insertNewObjectForEntityForName:@"Object" inManagedObjectContext:self.managedObjectContext];
  self.object = [newObject retain];

 }
 return self;
}


#pragma mark
#pragma mark -
#pragma mark Memory Management

-(void) dealloc {

    [object release], object=nil

 objectMapView.delegate = nil;
 [objectMapView release], objectMapView = nil;
 [super dealloc];
}

#pragma mark 
#pragma mark -
#pragma mark Enable/Disable Button and Textfield States

-(IBAction) updateSaveButtonState{

 if([objectNameTextField.text isEmptyOrWhitespace])
 {
  [saveButton setEnabled:NO];
 }
 else {
  [saveButton setEnabled:YES];
 }
}

#pragma mark
#pragma mark -
#pragma mark UITextField Delegate - Optional Method Implementations

- (void)textFieldDidEndEditing:(UITextField *)textField{ 

 [textField resignFirstResponder];

 if([textField isEqual:objectNameTextField])
 {
  if(![objectNameTextField.text isEmptyOrWhitespace])
  {
   [object setName:objectNameTextField.text];
  }
  else {
   [object setName:nil];
  }
 }
}

- (void)textFieldDidChange:(NSNotification*)aNotification{
 [self updateSaveButtonState];
}

#pragma mark
#pragma mark -
#pragma mark Core Data Persistance Management Procedures

- (IBAction)save {

 if([objectMapView annotations].count != 0)
 {
  NSLog(@"Cleaning up annotations");
  [objectMapView removeAnnotations:[objectMapView annotations]];
 }

 NSError *error = nil;

 //Double Check that all Object Information is Updated Before Saving
 [self textFieldDidEndEditing:NameTextField];

 if (![managedObjectContext save:&error]) {
  // Handle error
  exit(-1);  // Fail
 }  

 objectMapView.delegate = nil;

 [self.delegate newObjectViewController:self didAddObject:object];
}
- (IBAction)cancel {
 [managedObjectContext deleteObject:object];

 if([objectMapView annotations].count != 0)
 {
  NSLog(@"Trying to clean up %d annotations",[objectMapView annotations].count);
  [objectMapView removeAnnotations:[objectMapView annotations]];
 }

 NSError *error = nil;
 if (![managedObjectContext save:&error]) {
  // Handle error
  exit(-1);  // Fail
 }  

 objectMapView.delegate = nil;

 [self.delegate newObjectViewController:self didAddObject:nil];
}


#pragma mark
#pragma mark -
#pragma mark Location Notification Observer Management

-(void) addLocationObserversAndStartUpdatingLocation{ 
 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(locationDeniedNotification) name:@"LOCATION_DENIED_NOTIFICATION" object:nil];
 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(newLocationNotification) name:@"NEW_LOCATION_NOTIFICATION" object:nil];
 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(locationErrorNotification) name:@"LOCATION_ERROR_NOTIFICATION" object:nil];

 [[MyCLController sharedInstance].locationManager startUpdatingLocation]; 
}
-(void) removeLocationObserversAndStopUpdatingLocation{
 [[NSNotificationCenter defaultCenter] removeObserver:self name:@"LOCATION_DENIED_NOTIFICATION" object:nil];
 [[NSNotificationCenter defaultCenter] removeObserver:self name:@"NEW_LOCATION_NOTIFICATION" object:nil];
 [[NSNotificationCenter defaultCenter] removeObserver:self name:@"LOCATION_ERROR_NOTIFICATION" object:nil];

 [[MyCLController sharedInstance].locationManager stopUpdatingLocation];
}

#pragma mark
#pragma mark -
#pragma mark Location Notification Callback Procedures

-(void) newLocationNotification{  
 if(([[MyCLController sharedInstance] locationManager].location != NULL) && ([[MyCLController sharedInstance] locationManager].location != nil))
 {
  [self removeLocationObserversAndStopUpdatingLocation];

  // Add annotation to map 
  CustomMapAnnotation *annotation = [[CustomMapAnnotation alloc] initWithCoordinate:[[MyCLController sharedInstance] locationManager].location.coordinate title:@"Mark Location With Pin"]; 

  [self.objectMapView addAnnotation:annotation];
  [self.objectMapView selectAnnotation:annotation animated:YES];
  [annotation release];
 }
}
-(void) locationErrorNotification{
 [self removeLocationObserversAndStopUpdatingLocation];
}
-(void) locationDeniedNotification{ 
 [self locationErrorNotification];
}


#pragma mark
#pragma mark -
#pragma mark Location Update Procedures

-(IBAction) updateLocationWithPlacemark:(MKPlacemark*)placemark{

 NSDictionary *addressDictionary = [[placemark addressDictionary] retain];
 CLLocationCoordinate2D coordinate = [placemark coordinate];

 //Update Latitude
 [(GeoTag*)[(Location*)[object location] geoTag] setLatitude:[NSNumber numberWithDouble:coordinate.latitude]];

 //Update Longitude
 [(GeoTag*)[(Location*)[object location] geoTag] setLongitude:[NSNumber numberWithDouble:coordinate.longitude]];

 //Update Country
 [object setCountry:[addressDictionary objectForKey:@"CountryCode"]];

 //Update City
 [object setCity:[addressDictionary objectForKey:@"City"]];

 //Update State/Province Initials
 [object setStateOrProvince:[addressDictionary objectForKey:@"State"]];

 //Update ZipCode/PostalCode
 [object setZipCodeOrPostalCode:[addressDictionary objectForKey:@"ZIP"]];

 [addressDictionary release]; 
}

#pragma mark
#pragma mark -
#pragma mark Notification Observers

-(void) addObservers{
 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textFieldDidChange:) name:@"UITextFieldTextDidChangeNotification" object:nil];
 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(annotationCalloutInfoDidChange:) name:@"MKAnnotationCalloutInfoDidChangeNotification" object:nil];

}
-(void) removeObservers{
 [[NSNotificationCenter defaultCenter] removeObserver:self name:@"UITextFieldTextDidChangeNotification" object:nil];
 [[NSNotificationCenter defaultCenter] removeObserver:self name:@"MKAnnotationCalloutInfoDidChangeNotification" object:nil];

}

#pragma mark
#pragma mark -
#pragma mark Custom Annotation Update Notification Handler

-(void) annotationCalloutInfoDidChange:(NSNotification*)aNotification
{
 CustomAnnotation *annotation = (CustomAnnotation*)[aNotification object];
 [self updateLocationWithPlacemark:[annotation placemark]];

 [self updateSaveButtonState];
}

#pragma mark
#pragma mark -
#pragma mark MKMapViewDelegate Optional Implementations

- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation {

 if (annotation == mapView.userLocation) {
  return nil;
 }

 CustomAnnotationView *annotationView = (CustomAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:@"CustomAnnotation"];
 if (annotationView == nil) {
  annotationView = [[[CustomAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:@"CustomAnnotation"] autorelease];
 }

 return annotationView;
}
- (void)mapView:(MKMapView *)mapView didAddAnnotationViews:(NSArray *)views {}
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
{}

#pragma mark
#pragma mark -
#pragma mark View LifeCycle

-(void) viewDidLoad{

    [super viewDidLoad];

 [self addObservers];

 [self addLocationObserversAndStartUpdatingLocation];
}



@end

Additionally, here is the crash log:

Thread 0 Crashed:
0   libobjc.A.dylib                 0x00003ec0 objc_msgSend + 24
1   UIKit                           0x0005c8b0 -[UIImageView stopAnimating] + 76
2   UIKit                           0x0005c808 -[UIImageView dealloc] + 20
3   CoreFoundation                  0x0003963a -[NSObject release] + 28
4   MapKit                          0x0006b8a4 -[MKAnnotationView dealloc] + 80
5   CoreFoundation                  0x0003963a -[NSObject release] + 28
6   UIKit                           0x0000ab2c -[UIView(Hierarchy) removeFromSuperview] + 592
7   UIKit                           0x0005ca1c -[UIView dealloc] + 232
8   MapKit                          0x0003a814 -[MKOverlayView dealloc] + 804
9   CoreFoundation                  0x0003963a -[NSObject release] + 28
10  UIKit                           0x0000ab2c -[UIView(Hierarchy) removeFromSuperview] + 592
11  UIKit                           0x0005ca1c -[UIView dealloc] + 232
12  UIKit                           0x000cb870 -[UIScrollView dealloc] + 284
13  MapKit                          0x000699bc -[MKScrollView dealloc] + 88
14  CoreFoundation                  0x0003963a -[NSObject release] + 28
15  UIKit                           0x0000ab2c -[UIView(Hierarchy) removeFromSuperview] + 592
16  UIKit                           0x000cb4a0 -[UIScrollView removeFromSuperview] + 68
17  UIKit                           0x0005ca1c -[UIView dealloc] + 232
18  CoreFoundation                  0x0003963a -[NSObject release] + 28
19  UIKit                           0x0000ab2c -[UIView(Hierarchy) removeFromSuperview] + 592
20  UIKit                           0x0005ca1c -[UIView dealloc] + 232
21  MapKit                          0x00017794 -[MKMapView dealloc] + 1384
22  CoreFoundation                  0x0003963a -[NSObject release] + 28
23  UIKit                           0x0000ab2c -[UIView(Hierarchy) removeFromSuperview] + 592
24  UIKit                           0x0005ca1c -[UIView dealloc] + 232
25  CoreFoundation                  0x0003963a -[NSObject release] + 28
26  UIKit                           0x0000ab2c -[UIView(Hierarchy) removeFromSuperview] + 592
27  UIKit                           0x0005ca1c -[UIView dealloc] + 232
28  CoreFoundation                  0x0003963a -[NSObject release] + 28
29  UIKit                           0x0000ab2c -[UIView(Hierarchy) removeFromSuperview] + 592
30  UIKit                           0x0005ca1c -[UIView dealloc] + 232
31  CoreFoundation                  0x0003963a -[NSObject release] + 28
32  UIKit                           0x0000ab2c -[UIView(Hierarchy) removeFromSuperview] + 592
33  UIKit                           0x0005ca1c -[UIView dealloc] + 232
34  CoreFoundation                  0x0003963a -[NSObject release] + 28
35  Foundation                      0x00047990 NSPopAutoreleasePool + 238
36  QuartzCore                      0x0001e0fc run_animation_callbacks(double, void*) + 600
37  QuartzCore                      0x0001de64 CA::timer_callback(__CFRunLoopTimer*, void*) + 156
38  CoreFoundation                  0x000574bc CFRunLoopRunSpecific + 2192
39  CoreFoundation                  0x00056c18 CFRunLoopRunInMode + 44
40  GraphicsServices                0x0000436c GSEventRunModal + 188
41  UIKit                           0x00003c28 -[UIApplication _run] + 552
42  UIKit                           0x00002228 UIApplicationMain + 960
43  TestApp                         0x0000244a main (main.m:14)
44  TestApp                         0x000021d4 start + 44
+3  A: 
0   libobjc.A.dylib                 0x00003ec0 objc_msgSend + 24
1   UIKit                           0x0005c8b0 -[UIImageView stopAnimating] + 76
2   UIKit                           0x0005c808 -[UIImageView dealloc] + 20

Your app are crashed because you try call [UIImageView stopAnimating] after you release UIImageView.

Altermann
haha, yeah, i figured that out shortly after posting this but totally forgot to come back to it. Thanks though :)
TheAggie