Search code examples
iosblockweak-referencesretain-cycle

Block and retain cycle can't catch it



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


Solution

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