for循环中的内存泄漏(Objective-C iPhone)

fuz*_*uzz 0 memory iphone memory-leaks for-loop objective-c

在我的- (void)connectionDidFinishLoading:(NSURLConnection *)connection方法中,循环中出现了很多内存泄漏.只是想知道是否有人能够指导我如何减少发生的内存泄漏量?也许这不是最好的代码......对其他领域的任何帮助都将不胜感激.

//  SearchViewController.h

#import <UIKit/UIKit.h>
#import "VenueDetailViewController.h"
#import "OverlayViewController.h"
#import "TBXML.h"
#import "CoreLocationController.h"

@interface SearchViewController : UIViewController <UITableViewDelegate, UISearchBarDelegate, CoreLocationControllerDelegate> {
    VenueDetailViewController *venueDetailView;
    IBOutlet UITableView *tv;
    NSString *navBarTitle;
    NSMutableArray *venues;
    NSMutableArray *primaryCategories;
    NSString *categoryId;

    //Search properties.
    OverlayViewController *overlayView;
    IBOutlet UISearchBar *searchBar;
    BOOL letUserSelectRow;

    //Core location properties.
    CoreLocationController *CLController;

    BOOL searching;

    NSMutableData *responseData;
}

@property (nonatomic, retain) NSString *navBarTitle;
@property (nonatomic, retain) CoreLocationController *CLController;
@property (nonatomic, retain) VenueDetailViewController *venueDetailView;
@property (nonatomic, retain) OverlayViewController *overlayView;
@property (nonatomic, retain) IBOutlet UITableView *tv; 
@property (nonatomic, retain) NSMutableArray *venues;
@property (nonatomic, retain) NSMutableArray *primaryCategories;
@property (nonatomic, retain) NSString *categoryId;

- (void)doneSearching_Clicked:(id)sender;
- (void)findLocations:(id)sender;
- (void)loadPlacesWithLat:(NSString *)lat andLong:(NSString *)lng;
- (void)findPostcode:(NSString *)postcode;
- (void)showReloadButton;

@end

//  SearchViewController.m

#import "SearchViewController.h"
#import "GenericCell.h"
#import "FSVenue.h"
#import "AsyncImageView.h"
#import "Helper.h"
#import "JSON.h"

@implementation SearchViewController

@synthesize tv;
@synthesize venueDetailView, overlayView;
@synthesize CLController;
@synthesize navBarTitle;
@synthesize venues, primaryCategories;
@synthesize categoryId;

- (void)viewDidLoad {
    //Set the title.
    navBarTitle = @"Nearby Places";
    self.title = navBarTitle;

    //Set background and border to clear (to allow for background image to be visible).
    tv.backgroundColor = [UIColor clearColor];
    [tv setSeparatorColor:[UIColor clearColor]];

    //Add the search bar.
    tv.tableHeaderView = searchBar;
    searchBar.autocorrectionType = UITextAutocorrectionTypeNo;

    letUserSelectRow = YES;

    venues = [[NSMutableArray alloc] init];
    primaryCategories = [[NSMutableArray alloc] init];

    //Core location init.
    CLController = [[CoreLocationController alloc] init];
    CLController.delegate = self;

    //Add a refresh icon to the top right navigation bar.
    [self showReloadButton];

    if (self.categoryId != nil) {
        [self findLocations:nil];
    }

    searching = NO;

    [super viewDidLoad];
}

- (void)showReloadButton {
    UIBarButtonItem *refreshItem = [[UIBarButtonItem alloc]
                                    initWithBarButtonSystemItem:UIBarButtonSystemItemRefresh
                                    target:self
                                    action:@selector(findLocations:)];

    self.navigationItem.rightBarButtonItem = refreshItem;
    [refreshItem release];
}

#pragma mark -
#pragma mark Nearby Places / Core Location

- (void)findLocations:(id)sender {
    // Display loading overlay view.
    if (!searching) {
        [Helper beginLoading:self.view withTitle:navBarTitle];

        [self doneSearching_Clicked:nil];

        //Calls locationUpdate delegate method.
        [CLController.locMgr startUpdatingLocation];

        searching = YES;
    }
}

- (void)locationUpdate:(CLLocation *)location {
    NSString *lat;
    NSString *lng;
#if !(TARGET_IPHONE_SIMULATOR)
    lat = [NSString stringWithFormat:@"%f", location.coordinate.latitude];
    lng = [NSString stringWithFormat:@"%f", location.coordinate.longitude];
#else
    lat = @"-37.816016";
    lng = @"144.969717";
#endif

    [self loadPlacesWithLat:lat andLong:lng];
}

- (void)locationError:(NSError *)error {
    NSLog(@"locationError: %@", [error description]);
}

- (void)loadPlacesWithLat:(NSString *)lat andLong:(NSString *)lng {
    [CLController.locMgr stopUpdatingLocation];

    responseData = [[NSMutableData data] retain];
    NSString *url = [NSString stringWithFormat:@"https://api.foursquare.com/v1/venues.json?geolat=%@&geolong=%@", lat, lng];
    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:url]];
    [[NSURLConnection alloc] initWithRequest:request delegate:self];
}

- (void)findPostcode:(NSString *)pcode {
    //Webservice URL: http://ws.geonames.org/findNearbyPostalCodes?postalcode=2000&country=AU&style=SHORT&maxRows=1

    NSString *suburb;
    NSString *postcode;
    NSString *lat1;
    NSString *lng1;

    // load and parse an xml string.
    TBXML* tbxml = [[TBXML alloc] initWithURL:[NSURL URLWithString:
                                        [NSString stringWithFormat:@"http://ws.geonames.org/findNearbyPostalCodes?postalcode=%@&country=AU&style=SHORT&maxRows=1",
                                         pcode]]];

    // obtain root element.
    TBXMLElement *root = tbxml.rootXMLElement;

    // if root element is valid.
    if (root) {
        // search for the first geonames element within the root elements children.
        TBXMLElement *code = [TBXML childElementNamed:@"code" parentElement:root];

        if (code != nil) {
            // find the lat child element of the code element.
            TBXMLElement *lat = [TBXML childElementNamed:@"lat" parentElement:code];

            if (lat != nil) {
                lat1 = [TBXML textForElement:lat];
            }

            // find the long child element of the code element.
            TBXMLElement *lng = [TBXML childElementNamed:@"lng" parentElement:code];

            if (lng != nil) {
                lng1 = [TBXML textForElement:lng];
            }

            // find the postalcode child element of the code element.
            TBXMLElement *postalcode = [TBXML childElementNamed:@"postalcode" parentElement:code];

            if (postalcode != nil) {
                postcode = [TBXML textForElement:postalcode];
            }

            // find the postalcode child element of the code element.
            TBXMLElement *name = [TBXML childElementNamed:@"name" parentElement:code];

            if (name != nil) {
                suburb = [TBXML textForElement:name];
            }

            NSLog(@"Searching Postcode %@ (%@) ...", postcode, suburb);
            NSLog(@" Lat - %@", lat1);
            NSLog(@" Long - %@", lng1);

            [self loadPlacesWithLat:lat1 andLong:lng1];
        }
    }

    // release resources
    [tbxml release];
}

#pragma mark -
#pragma mark JSON Over HTTP

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
    [responseData setLength:0];
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
    [responseData appendData:data];
}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
    NSLog(@"connectin didFailWithError: %@", [error description]);
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
    [connection release];

    NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
    //[responseData release];

    NSDictionary *dictionary = [responseString JSONValue];
    [responseString release];

    NSArray *venueArray = [[[dictionary valueForKeyPath:@"groups"] objectAtIndex:0] valueForKeyPath:@"venues"];

    if ([dictionary valueForKeyPath:@"error"] != nil) {
        [Helper displayAlertMessage:[dictionary valueForKeyPath:@"error"] withTitle:@"Foursquare"];
    }

    for (id result in venueArray) {
        FSVenue *venue = [[FSVenue alloc] init];
        venue.name = [result valueForKeyPath:@"name"];
        venue.venueId = [result valueForKeyPath:@"id"];
        venue.geoLat = [result valueForKeyPath:@"geolat"];
        venue.geoLong = [result valueForKeyPath:@"geolong"];

        NSDictionary *primaryCategoryDict = [result valueForKeyPath:@"primarycategory"];

        FSPrimaryCategory *primaryCategory = [[FSPrimaryCategory alloc] init];
        primaryCategory.iconUrl = [primaryCategoryDict valueForKeyPath:@"iconurl"];
        primaryCategory.iconUrl = [primaryCategory.iconUrl stringByReplacingOccurrencesOfString:@".png" withString:@"_64.png"];
        primaryCategory.nodeName = [primaryCategoryDict valueForKeyPath:@"nodename"];
        primaryCategory.primaryCategoryId = [NSString stringWithFormat:@"%@", [primaryCategoryDict valueForKeyPath:@"id"]];

        //Check if categories match the category selected from the FSCategory controllers.
        if (self.categoryId != nil) {
            if ([self.categoryId isEqualToString:primaryCategory.primaryCategoryId]) {
                [venues addObject:venue];
                [venue release];
                [primaryCategories addObject:primaryCategory];
                [primaryCategory release];
            } else {
                [venue release];
                [primaryCategory release];
            }
        } else {
            [venues addObject:venue];
            [venue release];
            [primaryCategories addObject:primaryCategory];
            [primaryCategory release];
        }
    }

    [tv reloadData];

    //Hide loading overlay view.
    [Helper finishLoading:navBarTitle];

    searching = NO;
}

#pragma mark -
#pragma mark Table View

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return [venues count];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    static NSString *CellIdentifier = @"GenericCell";

    GenericCell *cell = (GenericCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    if (cell == nil) {
        NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:CellIdentifier owner:nil options:nil];

        for (id currentObject in topLevelObjects) {
            if ([currentObject isKindOfClass:[UITableViewCell class]]) {
                cell = (GenericCell *)currentObject;
                break;
            }
        }
    } else {
        AsyncImageView *oldImage = (AsyncImageView *)
        [cell.contentView viewWithTag:999];
        [oldImage removeFromSuperview];
    }

    FSPrimaryCategory *primaryCategory = (FSPrimaryCategory *)[primaryCategories objectAtIndex:indexPath.row];
    FSVenue *venue = (FSVenue *)[venues objectAtIndex:indexPath.row];

    AsyncImageView *asyncImage = [[[AsyncImageView alloc] initWithFrame:CGRectMake(3, 3, 48, 48)] autorelease];
    asyncImage.tag = 999;

    NSURL *url = [NSURL URLWithString:primaryCategory.iconUrl];
    [asyncImage loadImageFromURL:url];
    [cell.contentView addSubview:asyncImage];

    //The two images are 1x140 vertical gradients that UIKit automatically stretches horizontally to fit the width of the cell.
    cell.backgroundView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"Cell_1x140.png"]];
    cell.selectedBackgroundView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"CellSelected_1x140.png"]];

    cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
    cell.titleLabel.text = venue.name;

    return cell;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    if (venueDetailView == nil) {

        venueDetailView = [[VenueDetailViewController alloc] initWithNibName:@"VenueDetailViewController" bundle:[NSBundle mainBundle]];

        FSVenue *venue = (FSVenue *)[venues objectAtIndex:indexPath.row];

        venueDetailView.vid = venue.venueId;

        [self.navigationController pushViewController:venueDetailView animated:YES];
    }

    venueDetailView = nil;
    [venueDetailView release];
}

- (NSIndexPath *)tableView :(UITableView *)theTableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    if (letUserSelectRow)
        return indexPath;
    else
        return nil;
}

- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
    if (indexPath.row % 2) {
        [cell setBackgroundColor:[UIColor colorWithRed:((float)173 / 255.0f) green:((float)173 / 255.0f) blue:((float)176 / 255.0f) alpha:.60]];
    } else {
        [cell setBackgroundColor:[UIColor colorWithRed:((float)152 / 255.0f) green:((float)152 / 255.0f) blue:((float)156 / 255.0f) alpha:.60]];
    }

    cell.selectionStyle = UITableViewCellSelectionStyleGray;
}

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
    NSString *titleHeader;

    if ([venues count] == 0) {
        titleHeader = @"No venues were found.";
    } else {
        titleHeader = @"";
    }

    return titleHeader;
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    return 55;
}

#pragma mark -
#pragma mark Search Bar

- (void)searchBarTextDidBeginEditing:(UISearchBar *)theSearchbar {
    //Add the overlay view.
    if (overlayView == nil)
        overlayView = [[OverlayViewController alloc] initWithNibName:@"OverlayViewController" bundle:[NSBundle mainBundle]];

    CGFloat yaxis = self.navigationController.navigationBar.frame.size.height;
    CGFloat width = self.view.frame.size.width;
    CGFloat height = self.view.frame.size.height;

    //Parameters x = origin on x-axis, y = origin on y-axis.
    CGRect frame = CGRectMake(0, yaxis, width, height);
    overlayView.view.frame = frame;
    overlayView.view.backgroundColor = [UIColor grayColor];
    overlayView.view.alpha = 0.5;

    overlayView.searchView = self;

    [tv insertSubview:overlayView.view aboveSubview:self.parentViewController.view];

    letUserSelectRow = NO;
    tv.scrollEnabled = NO;
}

- (BOOL)searchBarShouldBeginEditing:(UISearchBar *)theSearchBar {
    searchBar.showsScopeBar = YES;
    [searchBar sizeToFit];

    [searchBar setShowsCancelButton:YES animated:YES];

    return YES;
}

- (BOOL)searchBarShouldEndEditing:(UISearchBar *)theSearchBar {
    searchBar.showsScopeBar = NO;
    [searchBar sizeToFit];

    [searchBar setShowsCancelButton:NO animated:YES];

    [self doneSearching_Clicked:nil];

    return YES;
}

- (void) doneSearching_Clicked:(id)sender {
    [searchBar resignFirstResponder];

    letUserSelectRow = YES;
    tv.scrollEnabled = YES;

    [overlayView.view removeFromSuperview];
    [overlayView release];
    overlayView = nil;

    //Reverse geocode postcode entered.
    if (![searchBar.text isEqualToString:@""]) {
        [self findPostcode:searchBar.text];
        searchBar.text = @"";
        [tv reloadData];
    }
}

- (void)searchBarCancelButtonClicked:(UISearchBar *)theSearchBar {  
    [self doneSearching_Clicked:nil];
}

- (void) searchBarSearchButtonClicked:(UISearchBar *)theSearchBar {
    [searchBar resignFirstResponder];
}

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    [tv reloadData];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
}

- (void)viewDidUnload {
    [super viewDidUnload];
}

- (void)dealloc {
    [navBarTitle release];
    [venueDetailView release];
    [CLController release];
    [tv release];
    [venues release];
    [primaryCategories release];
    [categoryId release];
    [responseData release];
    [super dealloc];
}

@end
Run Code Online (Sandbox Code Playgroud)

Ada*_*ght 5

如果self.categoryId != nil![self.categoryId isEqualToString:primaryCategory.primaryCategoryId],然后primaryCategoryvenue泄露.我只是考虑[primaryCategory release](和相同的venue)分支,并把它放在循环的末尾.

为了将来的帮助,你可能会喜欢XCode的"构建和分析"模式,它应该静态地检测这种代码流泄漏,并告诉你究竟在哪里分配泄漏.