2

I have the following Objective-C code:

NSString *urlStr=[[NSString alloc] initWithFormat:@"http://www.prestocab.com/driver/ajax/getFriendsOnMap.php"];
    NSURL *url=[NSURL URLWithString:urlStr];

    __block ASIFormDataRequest *request=[[ASIFormDataRequest alloc ]initWithURL:url];

    [request setDelegate:self];
    [request setPostValue:[NSString stringWithFormat:@"%f",swCoord.latitude ] forKey:@"sw_lat"];
    [request setPostValue:[NSString stringWithFormat:@"%f",swCoord.longitude ] forKey:@"sw_lng"];
    [request setPostValue:[NSString stringWithFormat:@"%f",neCoord.latitude ] forKey:@"ne_lat"];
    [request setPostValue:[NSString stringWithFormat:@"%f",neCoord.longitude ] forKey:@"ne_lng"];

    [request setCompletionBlock:^{
        NSLog(@"%@",[request responseString]);
        SBJsonParser *parser=[[SBJsonParser alloc]init];
        //NSDictionary *obj=[parser objectWithString:[request responseString] error:nil];
        NSDictionary *arr=[parser objectWithString:[request responseString] error:nil];

        MapViewAnnotation *annotation=[[MapViewAnnotation alloc]init];
        for(int i=0;i<arr.count;i++){
            NSDictionary *obj=[arr objectForKey:[NSString stringWithFormat:@"%d",i]];
            CLLocationCoordinate2D coord;
            coord.latitude=[[obj objectForKey:@"lat"] doubleValue];
            coord.longitude=[[obj objectForKey:@"lng"] doubleValue];
            [annotation initWithTitle:[obj objectForKey:@"uname"] andCoordinate:coord];

            //[self.mapView performSelectorOnMainThread:@selector(addAnnotation) withObject:annotation waitUntilDone:YES];
            [self.mapView addAnnotation:annotation];

        }
        [annotation release];
        //[self.mapView addAnnotations:annotations];
        //[annotations release];
    }];
    [request setFailedBlock:^{

    }];
    [request startAsynchronous];

As you can see, I'm getting some data from my website using ASIHttpRequest, parsing the result and hoping to put annotations on the MKMapView.

Trouble is, when I call [self.mapView addAnnotation:...] I keep getting one of these EXC_BAD_ACCESS errors that I simply cannot get to the bottom of.

Anyone have any suggestions?

Many thanks in advance,

Eamorr
  • 9,872
  • 34
  • 125
  • 209
  • You are re-using the very same annotation instance over and over again. Not knowing the API here, I can only guess, but it smells like a mistake. – Dirk Sep 25 '11 at 16:03
  • OK, I put the annotation instance inside the for loop and I still get the same error... – Eamorr Sep 25 '11 at 16:05
  • Probably won't fix your issue, but you're leaking a lot of memory. Your init method in the loop ( `[annotation initWithTitle:[obj objectForKey:@"uname"] andCoordinate:coord];` ) is adding a +1 count every time but you're not balancing each init with a release. – FeifanZ Sep 25 '11 at 16:50

2 Answers2

3

By the time the completion block runs self may not be valid. In such a case you need to copy the block to the heap.

You can wrap your block:

[ <your block> copy];

Then there is the issue of releasing the block, many times an auto release works well:

[[ <your block> copy] autorelease];

Other times you may need to explicitly release it.

You might want to typedef your block to make this clearer.

zaph
  • 111,848
  • 21
  • 189
  • 228
  • Hi, could you tell me how to do that please? – Eamorr Sep 25 '11 at 17:34
  • Hey, many thanks for that. I've discovered that if I make initWithTitle:@"asdf" it works fine! However, if I replace @"asdf" with [obj objectForKey:@"uname"], it crashes! I've done an NSLog of [obj objectForKey:@"uname"] and it seems fine! **confused** – Eamorr Sep 25 '11 at 18:00
  • 1
    Constant strings have retain counts such that they never release. `[obj objectForKey:@"uname"]` returns an autoreleased object. That indicates that the runloop is being called before you use the string and the autorelease is causing a release of the object. You need to hold onto the string until the block completes. I do not use the ASI* code and can't help with that. – zaph Sep 25 '11 at 18:04
  • OK, that kinda makes sense, but do you have any suggestions as to how I might fix this problem? You can probably tell I'm only a novice iPhone programmer... – Eamorr Sep 25 '11 at 18:05
1

you get BAD_ACCESS because either the mapView or the annotation is already destroyed but you have a pointer to that instances. I guess that you dont retain the mapView. You can check these kind of errors by turning on the NSZombies and by enabling stop on exception.

Or put this code before the line of code where the error happens:

NSLog(@"mapView-desc: %@",[self.mapView description]);
NSLog(@"annotation-desc: %@",[annotation description]);

The BAD_ACCESS should now happen in one of these two lines and then you know on which obejct you have forgotten to retain ;) If there is all fine then the problem is inside mapView or the data in your annotations. The easiest way is to enable NSZombies and wait for messages in your console.

Community
  • 1
  • 1
thomas
  • 5,637
  • 2
  • 24
  • 35
  • Hi, when I do that, I get the following mapView-desc: > 2011-09-25 18:35:19.010 PrestoCab14[48323:e903] annotation-desc: – Eamorr Sep 25 '11 at 17:36
  • ah, ok, then we have to search further for a released object. At first: are there any warnings during compile-time? and enable zombies and stop-on-exception and show the piece of code and the message thrown by the zombie. It is also an interesting piece of information if BAD_ACCESS is thrown without message from NSZombie – thomas Sep 25 '11 at 19:08
  • I'll have to look into this enabling zombies stuff. This is new to me. Thanks for your help! – Eamorr Sep 25 '11 at 19:46
  • it is very easy to enable. If the Zombies are enabled each object which should be destroyed is replaced by a zombie-object. If you are accessing that zdeallocated object you will get a zombie which will raise an exception immediatelly. This tells you which object you are accessing which would be already deallocated in normal mode and where in your code. – thomas Sep 26 '11 at 00:12