MKAnnotationView memory leak issue - iphone

I needed recently to test my app with Allocations because of memory warnings. Even there are no leaks, the heap keeps growing with annotations added to the map. Every time I zoom in or out, the old annotations are removed, new ones are created and added to the map :
All of the memory locations from the NumberedAnnotationView group show the marked line as the problematic in viewForAnnotation
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation{
static NSString *reuseId_big = #"bigcircle";
NumberedCircleAnnotationView * nca = nil;
//nca = (NumberedCircleAnnotationView *)[self.mapView dequeueReusableAnnotationViewWithIdentifier:reuseId_big];
if ( nca == nil )
nca = [[[NumberedCircleAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:reuseId_big imageType:1] autorelease]; // THIS line
nca.delegate = self;
}
return nca;
}
The init looks like this :
-(id)initWithAnnotation:(id<MKAnnotation>)annotation reuseIdentifier:(NSString *)reuseIdentifier imageType:(int)imageType {
self = [super initWithAnnotation: annotation reuseIdentifier: reuseIdentifier]; // THIS line
if (self != nil)
{
// set stuff
}
return self;
}
Even after minutes, these autoreleased objects are still there. ( 17 and 24 is the number of annotations displayed on map and removed with [mapView removeAnnotations:[mapView annotations]]; each time I zoom in/out.
The others, I think, are some MapKit generated stuff. I'm experiencing this in the simulator with versions 5.0 and 5.1.
How could I fix this ? Is something that I'm missing ? Or is this the normal behavior of Mapkit ?
Thanks!

Is there any reason you are not using the
[mapView dequeueReusableAnnotationViewWithIdentifier:annotationIdentifier];
method?
Seems like you are creating view for your annotations every time!
Check out this article:
http://www.highoncoding.com/Articles/804_Introduction_to_MapKit_Framework_for_iPhone_Development.aspx

Related

User location image pin disappears most of time

i Am using following code
-(MKAnnotationView *) mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation
{
if ([[annotation title] isEqualToString:#"Current Location"] )
{
MKAnnotationView *anView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"currentPin"];
anView.image = [UIImage imageNamed:#"pin_green.png"];
anView.canShowCallout = true;
anView.enabled = true;
return anView;
}
The issue is, it randomly disappears and appears again. Giving a very bad user experience. Any way to fix this?
There are several suspect things about this code:
You're not using dequeue, as someone has pointed out. In particular, the problem here is that you are making a new view every single time, rather than checking to see whether a new view needs making.
You are forgetting the key step, namely, to associate the view with the annotation.
Here is the canonical structure of a simple viewForAnnotation: implementation where we supply our own view:
- (MKAnnotationView *)mapView:(MKMapView *)mapView
viewForAnnotation:(id <MKAnnotation>)annotation {
MKAnnotationView* v = nil;
if ([annotation.title isEqualToString:#"Current Location"]) {
static NSString* ident = #"greenPin";
v = [mapView dequeueReusableAnnotationViewWithIdentifier:ident];
if (v == nil) {
v = [[MKAnnotationView alloc] initWithAnnotation:annotation
reuseIdentifier:ident];
v.image = [UIImage imageNamed:#"pin_green.png"];
v.canShowCallout = YES;
}
v.annotation = annotation;
}
return v;
}
Since that code works for me, I'd suggest you start with it and tweak it as necessary.
By the way, you do NOT need this method just to get a green pin! You do know that, right? iOS will give you a green pin (MKPinAnnotationColorGreen).
You should use MKMapView's dequeueReusableAnnotationViewWithIdentifier: and see if you get a view back before creating a new one with initWithAnnotation:reuseIdentifier::
MKAnnotationView *anView = [mapView dequeueReusableAnnotationViewWithIdentifier:#"currentPin"];
if (!anView) {
anView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"currentPin"];
anView.image = [UIImage imageNamed:#"pin_green.png"];
anView.canShowCallout = true;
anView.enabled = true;
}
return anView;
That said, I'm not entirely sure this is the cause of your problem.

ios6 MKMapView deselection bug

I observed a strange behavior with the map in iOS 6.
Here is a code which adds a single annotation, absolutely nothing else in the project:
- (void)viewDidLoad
{
[super viewDidLoad];
MKPointAnnotation * p = [[MKPointAnnotation alloc] init];
p.coordinate = CLLocationCoordinate2DMake(10, 10);
p.title = #" test test";
[self.mapView addAnnotation:p];
self.mapView.centerCoordinate = p.coordinate;
}
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id < MKAnnotation >)annotation
{
MKPinAnnotationView * p = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"wassup" ];
p.canShowCallout = YES;
p.pinColor = MKPinAnnotationColorGreen;
return p;
}
When it is selected and I move (pan) the map small distances like 0.5-1 cm, on iOS6 the annotation gets deselected. On earlier versions works as expected and remains selected.
Is it a bug ? Is there any workaround for this ?
Thanks !
Apple maps are full of bugs.. LOL you noticed that too..
Its a feature of iOS 6 sometime can irritate the the user.. :)
in annotaionView method use:
// iOS6 BUG WORKAROUND !!!!!!!
if (is6orMore) {
[annotationView setTransform:CGAffineTransformMakeRotation(.001)]; //any small positive rotation
}
Also see the link
If you want to not deselect an annotation view, you should subclass MKAnnotationView and override the setSelected:animated: method and stop the annotation view from being deselected.

Centralized the code

- (void) setTheMap
{
myMap.mapType = MKMapTypeStandard;
[myMap setDelegate:self];
CLLocationCoordinate2D coord = {latitude:myValue , longitude:myValue };
MKCoordinateSpan span = {latitudeDelta:0.05f , longitudeDelta: 0.05f};
MKCoordinateRegion region = {coord , span};
[myMap setRegion:region];
[self.view addSubview:myMap];
PantryAnnotation *dis = [[PantryAnnotation alloc] init];
dis.coordinate = region.center;
dis.title = row.panName;
dis.subTitle = row.geo_lat;
[myMap addAnnotation:dis];
}
- (MKAnnotationView *)mapView:(MKMapView *)mV viewForAnnotation:(id<MKAnnotation>)annotation
{ MKPinAnnotationView *pview = nil;
if (annotation != myMap.userLocation)
{
static NSString *defalt = #"Volunteer";
pview = (MKPinAnnotationView *)[myMap dequeueReusableAnnotationViewWithIdentifier:defalt];
if ( pview == nil )
pview = [[[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:defalt] autorelease];
pview.pinColor = MKPinAnnotationColorRed;
pview.canShowCallout = YES;
pview.animatesDrop = YES;
}
else
{
[myMap.userLocation setTitle:#"I am Here"];
}
return pview;
}
The above gives me map view with annotation pin. I have to write this code in more than 8 files. Can any one tell me how can I centralized this code ? I mean to create only one class and by object of that class I can add the map in any other view.
Thanks..
Since the rest of your code is not clear, it is hard to give advice, but you may try to analyze it first;
what is common in these 8 code parts, and what is different?
Then you should figure out if you need to create and hold references to any local variables, lets say your map objects, or something like that.
If so, then subclassing would be the first choice to look at,
if not then an Obj-C category would probably do (http://macdevelopertips.com/objective-c/objective-c-categories.html). P.S: you cannot add member fields with this, only behavior (methods) are allowed.
If you need only objects with little to none behavior other than these, then you may make them plain objects holding the parameters only.
Or even you may use static methods like ( +(void) blahblah; ) just to be short, but dont do it unless you are sure that the behavior is generic in nature...
Well, a soup of advice here, if you provide more info, i can write in one of these directions in more details
good luck!

Trying to Show User Location on Mapkit, the most annoying thing ever. Anyone lend a hand?

I'm following this tutorial (http://icodeblog.com/2009/12/21/introduction-to-mapkit-in-iphone-os-3-0/) on adding mapkit and annotations to an application. However, i'm seriously struggling with the User Location. I'm new to xcode so not quite sure what to do next. I have tried Tony's option:
step one: add the CoreLocation framework to the project.
Step two: add this function to the iCodeMapViewController.m:
- (void)setCurrentLocation:(CLLocation *)location {
MKCoordinateRegion region = {{0.0f, 0.0f}, {0.0f, 0.0f}};
region.center = location.coordinate;
region.span.longitudeDelta = 0.15f;
region.span.latitudeDelta = 0.15f;
[self.mapView setRegion:region animated:YES];
}
step three: add this code to the ViewForAnnotation Method:
if (annotation != mapView.userLocation) {
//the rest of the ViewForAnnotation code goes here
}else{
CLLocation *location = [[CLLocation alloc]
initWithLatitude:annotation.coordinate.latitude
longitude:annotation.coordinate.longitude];
[self setCurrentLocation:location];
}
But when i go to build, it doesn't like it.
I've also tried this option:
-(MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id )annotation
{
if ([annotation isKindOfClass:MKUserLocation.class]) return nil;
//rest of code
}
The blue dot shows, my custom annotations show but the app crashes when i try and scroll through the table. The debugger gives no help but does stop on this statement.
Can someone please help? With code examples too? i think the answer to this post might be useful to a number of people also struggling with the mapkit.
Cheers
I had the same problem, but I managed to solve it.
In cellForRowAtIndexPath I did this:
NSMutableArray *annotations = [[NSMutableArray alloc] init];
if(indexPath.section == 0)
{
for(MinuAsukohad *annotation in [mapView annotations])
{
if(![annotation isKindOfClass:[MKUserLocation class]])
{
if([annotation annotationType] == MinuAsukohadTypeInterest)
{
[annotations addObject:annotation];
}
}
}
cell.textLabel.text = [[annotations objectAtIndex:indexPath.row] title];
}
You just have to repeat it for all the sections.
Sounds like you are trying to include your current location as one of the cells in the table ... look at your console and give us the output when the crash happens.

iphone mkannotationview strange problem - possibly being reused

I've just found a strange issue with my sub classed mkannotationview.
When I add the first 5 markers, they all work perfectly. In the mkannotationview sub class, I NSLog a message which I see 5 times. However, when I remove ALL the markers and redraw them - using all the same methods, I see the NSLog only once.
It's like the map is reusing existing annotationviews? Is there a way to force it to use new ones each time?
[UPDATE with code]
So the reason I cannot reuse (and this may or may not be the problem) is that I am creating unique markers with a label. The label on the marker contains a reference to the individual marker (consider it like a product ID)
So... in ProductPlot.h
#interface ProductPlot : NSObject <MKAnnotation> {
NSString *productID;
float latitude;
float longitude;
}
#property (nonatomic, copy) NSString *productID;
and ProductPlot.m
#implementation ProductPlot
#synthesize productID;
- (CLLocationCoordinate2D)coordinate {
CLLocationCoordinate2D coord = {self.latitude, self.longitude};
return coord;
}
- (NSString *) productID {
return productID;
}
then I have the annotation view sub classed as ProductPlotView.h
#interface ProductPlotView : MKAnnotationView {
ProductPlot *product;
}
and in ProductPlotView.m
#implementation ProductPlotView
- (id)initWithAnnotation:(id <MKAnnotation>)annotation reuseIdentifier:(NSString *)reuseIdentifier {
if(self = [super initWithAnnotation:annotation reuseIdentifier:reuseIdentifier]) {
product = (ProductPlot *)annotation;
UILabel *plate2 = [[[UILabel alloc] init] autorelease];
plate2.text = product.productID;
plate2.frame = CGRectMake(35, 4, 100, 30);
plate2.backgroundColor = [UIColor clearColor]; //]clearColor];
[plate2 setFont: [UIFont fontWithName: #"myFont" size: plate2.font.pointSize]];
[self addSubview:plate2];
}
self.frame = CGRectMake(0,0,133,40);
return self;
}
So then in my code, I plot the points using
- (void)plotPoint: (int) y latitude: (double) lat longitude: (double) lng productID: (NSString *) pID {
ProductPlot *newAnnotation = [[ProductPlot alloc] init];
newAnnotation.latitude = lat;
newAnnotation.longitude = lng;
newAnnotation.productID = pID;
[mapView addAnnotation:newAnnotation];
[newAnnotation release];
}
I also have the code to handle the annotations.
- (MKAnnotationView *)mapView:(MKMapView *)lmapView viewForAnnotation:(id <MKAnnotation>)annotation {
ProductPlotView *eventView = (ProductPlotView *)[lmapView
dequeueReusableAnnotationViewWithIdentifier:#"eventview"];
if(eventView == nil) {
eventView = [[[VehicleViewInfo alloc] initWithAnnotation:annotation
reuseIdentifier:#"eventview"]
autorelease];
}
eventView.annotation = annotation;
return eventView;
}
So... the above will take the productID and place it on a label which is the map marker. This seems to work perfectly on the FIRST plot instance, but if I remove the markers (using [mapView removeAnnotations:mapView.annotations];) then call this function again, it draws the points OK, but the product IDs are not correct. It appears to draw them at random.
NOTE: the code above has had a few
parts removed, so there may be typos
Thanks for any info.
To answer my own question, I added MKAnnotationView* annotationView = nil; to the top of my - (MKAnnotationView *)mapView:(MKMapView *)lmapView viewForAnnotation:(id <MKAnnotation>)annotation code.
Now it seems to be working perfectly. I'd still be keen to see if others think that this is the correct way to do this?
I solved a similar problem checking subviews on annotation view.
if it hasn't subviews i create a new label and add it as subview, if it has subviews i take the first and edit the label.text property.
In this way subviews are always zero or only one.