0


I've got an issue with blocks and weak reference, I'm under ARC. I built a class it is a free project that is a kind of easy wrapper around Google Directions API you can download it here:link to the project
Im'm using it inside a view controller the problem is that after using it the view controller is not deallocated. I guess that is an issue with this object because if I comment it out or set to nil everything works correctly. I'm not able to understand where is the retain cycle, of course I set to weak self, here is the method of the view controller where I use it:

- (void) getDirections{
 __weak RouteMapViewController *  weakSelf =  self;
self.routeObject = [[RouteDirectionsObject alloc]init];

[self.mapView removeAnnotations:self.mapView.annotations];
[self.mapView removeOverlays:self.mapView.overlays];
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
[_routeObject createDirectionRequestWithStartPoint:weakSelf.startPoint
                                           andEndPoint:weakSelf.endPoint
                                     withCallBackBlock:^(NSError *error, NSDictionary *routeDistance, NSDictionary *routeDuration, MKPolyline *routePolyline, NSArray *routes, NSArray *steps, CLLocation *startPoint, CLLocation *endPoint, NSArray *directions) {
    [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
    Annotation * startAnnotation = [[Annotation alloc]initWithCoordinate:startPoint.coordinate title:NSLocalizedString(@"YOUR_POSITION_KEY", @"Your position") annotationType:AnnotationTypeStart];
    Annotation * endAnnotation = [[Annotation alloc]initWithCoordinate:endPoint.coordinate title:NSLocalizedString(@"AIRPORT_POSITION_KEY", @"Airport position") annotationType:AnnotationTypeEnd];
    NSArray * annotationArray = [NSArray arrayWithObjects:startAnnotation, endAnnotation, nil];
    weakSelf.routeSteps = steps;
    weakSelf.routeDirections = directions;
    weakSelf.duration = routeDuration;
    weakSelf.distance = routeDistance;
    CLLocationDegrees maxLat = -90.0f;
    CLLocationDegrees maxLon = -180.0f;
    CLLocationDegrees minLat = 90.0f;
    CLLocationDegrees minLon = 180.0f;

    for (int i = 0; i < weakSelf.routeSteps.count; i++) {
        NSDictionary * stepDictCoordinate = [[weakSelf.routeSteps objectAtIndex: i]objectForKey:@"start_location"];
        CLLocationCoordinate2D currentLocationCoordinate = CLLocationCoordinate2DMake([[stepDictCoordinate objectForKey:@"lat"]doubleValue], [[stepDictCoordinate objectForKey:@"lng"]doubleValue]);
        if(currentLocationCoordinate.latitude > maxLat) {
            maxLat = currentLocationCoordinate.latitude;
        }
        if(currentLocationCoordinate.latitude < minLat) {
            minLat = currentLocationCoordinate.latitude;
        }
        if(currentLocationCoordinate.longitude > maxLon) {
            maxLon = currentLocationCoordinate.longitude;
        }
        if(currentLocationCoordinate.longitude < minLon) {
            minLon = currentLocationCoordinate.longitude;
        }
    }

    MKCoordinateRegion region;
    region.center.latitude     = (maxLat + minLat) / 2;
    region.center.longitude    = (maxLon + minLon) / 2;
    region.span.latitudeDelta  = maxLat - minLat;
    region.span.longitudeDelta = maxLon - minLon;


    dispatch_async(dispatch_get_main_queue(), ^{
        if ( error) {
            UIAlertView * alert = [[UIAlertView alloc]initWithTitle:NSLocalizedString(@"Error", @"Error alert view title") message:NSLocalizedString(@"KEY_DIRECTIONS_ERROR", @"Alert error message for directions") delegate:nil cancelButtonTitle:@"OK" otherButtonTitles: nil];
            [alert show];

            [_routesButton setEnabled:NO];

        }
        else{
            [weakSelf.mapView addAnnotations:annotationArray];
            [_routesButton setEnabled:YES];

            if(routePolyline){
                [weakSelf.mapView addOverlay:routePolyline];
            }
            else{
                UIAlertView * alert = [[UIAlertView alloc]initWithTitle:NSLocalizedString(@"Error", @"Error alert view title") message:NSLocalizedString(@"KEY_DIRECTIONS_POLYLINE_ERROR", @"Polyline inconsistant") delegate:nil cancelButtonTitle:@"OK" otherButtonTitles: nil];
                [alert show];

            }
            //[weakSelf.mapView setRegion:region animated:YES];
            [weakSelf setRegion:region];
        }
    });
}];}

If I put breakpoints and ask for the view controller's retainCount I can see that is incremented different times even if the view controller passed is set to weak. Any help will be really appreciated.
Thanks,
Andrea

/************UPDATE***********/**
Checking with allocations I can see that inside the block the view controller is retained a lot of times passing trought a method called -tryRetain till is decremented but it seems to miss one release for deallocation. For sake I must specify that the block passed is copied in the class route direction object. I made a little sample that you can download here:download project

Andrea
  • 26,120
  • 10
  • 85
  • 131
  • Also you shouldn't use instance variables (in blocks) of self class, _routerButton for example. – NeverBe Nov 14 '12 at 07:39
  • OMG... here it is commenting the two _routeButton instances everything is dealloced correctly, I thought the were just copied as single instances. please write it as answer that i check it. Thank you so much. – Andrea Nov 14 '12 at 10:05

3 Answers3

1

The absolute retain counts of objects are meaningless; www.whentouseretaincount.com (there are some links at the bottom describing the technical details).

It is unlikely that the Leaks instrument will help. It might, but maybe not. The Allocations Instrument, though, is your friend. Turn on "record reference counts" and "only track active allocations". Run your app in Instruments and then look for the objects that should go away, but aren't. Clicking through any one will show the retain/release events for that object which will answer the question of where the extra retains are coming from.

More likely than not, given that this is a view object, it is because it is still in the view hierarchy, but buried behind other, opaque, views. It might also be held as the target of a timer or in a cache, potentially a "go back" style navigation cache.

bbum
  • 162,346
  • 23
  • 271
  • 359
  • Hello, thanks for you answer. I've checked the project using allocation and it seems that the view controller is retained a lot of time entering the method, I can clearly see in stack that is calling a method -tryRetain plus others related to events. I've updated the answer with a sample project, to check for other object that retains the view controller. As you can see, you push the view controller withe the map see the route and when you pop it is not deallocated even if there is non reference inside the navigation controller stack. – Andrea Nov 14 '12 at 07:36
0

You can use Instruments to determine what is retaining and releasing your object, and figure out where the imbalance is occurring. This can be done by selecting Profile when building and running your project, and you'll boot up instruments.

Once in, you can select Leaks to determine where your leak is occurring. enter image description here

A couple of guides you can use to get a grip of Instruments are:

WDUK
  • 18,870
  • 3
  • 64
  • 72
  • Thanks WDUK, unfortunately it doesn't change. The R.C. is the same.Before entering the object it has an RC of 1, when it comes out is 6. – Andrea Nov 13 '12 at 14:31
  • Have you tried keeping track of what's retaining it in Instruments? You can see where it's being retained and released, and then find where the imbalance is occurring? Will update answer to reflect this approach. – WDUK Nov 13 '12 at 14:36
  • Sure, it's incredible.. in the RC history of the object I can clearly see that it goes inside the block and receive a lot of increments, it's also weird that the reference count I get manually is different from the one showed in leaks – Andrea Nov 13 '12 at 15:27
  • Instruments is the more reliable source; in NSObject's protocol reference (https://developer.apple.com/library/ios/#documentation/Cocoa/Reference/Foundation/Protocols/NSObject_Protocol/Reference/NSObject.html#//apple_ref/occ/intf/NSObject), it does state that `retainCount` "is of no value in debugging memory management issues.", It also clearly states "Do not use this method", as it is now obsolete! – WDUK Nov 13 '12 at 15:47
-1

You shouldn't use instance variables (in blocks) of self class, _routerButton in your case

NeverBe
  • 5,213
  • 2
  • 25
  • 39
  • @WDUK http://developer.apple.com/library/ios/#DOCUMENTATION/Cocoa/Conceptual/Blocks/Articles/bxVariables.html – NeverBe Nov 14 '12 at 16:25
  • In that case, it's not a case you shouldn't use instance variables from self, more that you should access by value instead of reference. So instead of using `instanceVariable`, use `id localVariable = instanceVariable;` and reference localVariable instead. It's at the bottom of that page. – WDUK Nov 14 '12 at 17:04