0

In my iPhone app, having a UITableView with custom cells. Which contains UILabels and UIImageViews. But I am getting memory leak when I assign the image to the image view. Here is the code. Leak is in the method cellForRowAtIndexPath: I have mentioned the leak percentage. Please check the code, what went wrong here? Please help.

        //  UIMenuItemCell.h
            @class UIMenuitemImage;
            @interface UIMenuItemCell : UITableViewCell{
                UILabel *cellItemName;
                UIImageView *cellitemImage;
            }
            @property (nonatomic, retain) UILabel *cellItemName;
            @property (nonatomic, retain) UIImageView *cellitemImage;

        //  UIMenuItemCell.m

        #import "UIMenuItemCell.h"

        @implementation UIMenuItemCell
        @synthesize cellItemName, cellitemImage, cellItemButton, cellItemProgress;

        - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
        {
            self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
            if (self) {
                // Initialization code
        //        cellitemImage = [[UIMenuitemImage alloc]init];
            }
            return self;
        }

        - (void)setSelected:(BOOL)selected animated:(BOOL)animated  
        {
            [super setSelected:selected animated:animated];

            // Configure the view for the selected state
        }

    //  MenuScreenVC.m
    - (UIMenuItemCell *) getCellContentView:(NSString *)cellIdentifier {

    CGRect CellFrame = CGRectMake(0, 0, 150, 60);
    CGRect Label1Frame = CGRectMake(20, 23, 98, 30);
    CGRect imgFrame = CGRectMake(20, 48, 110, 123);
    CGRect btnFrame = CGRectMake(25, 136, 100, 30);
    CGRect progressFrame = CGRectMake(25, 140, 100, 21);

    UILabel *lblTemp;
    UIImageView *itemImg;
    UIButton *itemBtn;
    UIProgressView *itemProgView;

    UIMenuItemCell *cell = [[[UIMenuItemCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier] autorelease];
    cell.frame = CellFrame;

    //Initialize Label with tag 1.  
    lblTemp = [[UILabel alloc] initWithFrame:Label1Frame];
    lblTemp.tag = 1;
    lblTemp.textColor=[UIColor colorWithRed:139.0f/255.0f green:69.0f/255.0f blue:19.0f/255.0f alpha:1.0f];
    lblTemp.textAlignment = UITextAlignmentCenter;
    lblTemp.backgroundColor = [UIColor clearColor];
    lblTemp.font = [UIFont systemFontOfSize:13.0];
    [cell.contentView addSubview:lblTemp];
    [lblTemp release];

    //Initialize ImageView
    itemImg = [[UIImageView alloc]initWithFrame:imgFrame];
    itemImg.tag = 2;
    [cell.contentView addSubview:itemImg];
    [itemImg release];

    //Initialize Button
    itemBtn = [[UIButton alloc]initWithFrame:btnFrame];
    itemBtn.frame = btnFrame;
    itemBtn.tag = 3;
    itemBtn.titleLabel.textColor = [UIColor blueColor];
    itemBtn.titleLabel.font = [UIFont systemFontOfSize:9.0];
    [cell.contentView addSubview:itemBtn];
    [itemBtn release];

    //Initialize ProgressView
    itemProgView = [[CustomProgressView alloc]initWithFrame:progressFrame];
    itemProgView.tag = 4;
    //[cell.contentView addSubview:itemProgView];
    [itemProgView release];

    return cell;
}
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{

    NSString *CellIdentifier = [NSString stringWithFormat:@"Cell%d", indexPath.row];

    UIMenuItemCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    if(cell == nil){
        cell = [self getCellContentView:CellIdentifier];

        cell.transform = CGAffineTransformMakeRotation(M_PI_2);
        cell.selectionStyle = UITableViewCellSelectionStyleNone;

        cell.cellItemName = (UILabel *)[cell viewWithTag:1];
        cell.cellitemImage = (UIImageView *)[cell viewWithTag:2];
        cell.cellItemButton = (UIButton *)[cell viewWithTag:3];
        cell.cellItemProgress = (UIProgressView *)[cell viewWithTag:4];

        DataBaseClass *itemObj = [appDelegate.itemArray objectAtIndex:indexPath.row];

        __autoreleasing NSString *imageLocalFilePath = nil;
        if ([[tempitemStatusArray objectAtIndex:indexPath.row] isEqualToString:@"NotAvailable"]) {
            cell.cellItemProgress.hidden = YES;
            cell.cellItemButton.hidden = NO;
            imageLocalFilePath = [NSString stringWithFormat:@"%@",[tempItemLocalNotAvailPath objectAtIndex:indexPath.row]];
            NSString *date = [self changeDateFormat:itemObj.itemReleaseDate];          
            [cell.cellItemButton setTitle:date forState:UIControlStateNormal]; 
            cell.cellItemButton.userInteractionEnabled = NO;
            cell.userInteractionEnabled = NO;
            [cell.cellItemButton removeTarget:nil action:NULL forControlEvents:UIControlEventAllEvents];
            [cell.cellItemButton setBackgroundImage:[UIImage imageNamed:@"not_available_bttn_bck_img"] forState:UIControlStateNormal];
        }else if ([[tempitemStatusArray objectAtIndex:indexPath.row] isEqualToString:@"Available"]){
            cell.cellItemButton.userInteractionEnabled = YES;
            cell.userInteractionEnabled = YES;
            cell.cellItemProgress.hidden = YES;
            [cell.cellItemButton setTitle:@"" forState:UIControlStateNormal];
            [cell.cellItemButton setBackgroundImage:[UIImage imageNamed:@"available_bttn_img_normal"] forState:UIControlStateNormal];
            [cell.cellItemButton setBackgroundImage:[UIImage imageNamed:@"available_bttn_img_pressed"] forState:UIControlStateHighlighted];
            [cell.cellItemButton removeTarget:nil action:NULL forControlEvents:UIControlEventAllEvents];
            [cell.cellItemButton addTarget:self action:@selector(confirmationAlert:) forControlEvents:UIControlEventTouchUpInside];
            imageLocalFilePath = [NSString stringWithFormat:@"%@",[tempItemLocalAvailPath objectAtIndex:indexPath.row]];
        }else if ([[tempitemStatusArray objectAtIndex:indexPath.row] isEqualToString:@"Active"]) {
            cell.cellItemButton.userInteractionEnabled = YES;
            cell.userInteractionEnabled = YES;
            cell.cellItemProgress.hidden = YES;
            [cell.cellItemButton setTitle:@"" forState:UIControlStateNormal];
            [cell.cellItemButton setBackgroundImage:[UIImage imageNamed:@"active_bttn_img_normal"] forState:UIControlStateNormal];
            [cell.cellItemButton removeTarget:nil action:NULL forControlEvents:UIControlEventAllEvents];
            [cell.cellItemButton addTarget:self action:@selector(alert) forControlEvents:UIControlEventTouchUpInside];
            imageLocalFilePath = [NSString stringWithFormat:@"%@",[tempItemLocalAvailPath objectAtIndex:indexPath.row]];
        }else if([[tempitemStatusArray objectAtIndex:indexPath.row] isEqualToString:@"Downloading"]) {
            imageLocalFilePath = [NSString stringWithFormat:@"%@",[tempItemLocalAvailPath objectAtIndex:indexPath.row]];
            [cell.contentView addSubview:myprogressView];
            cell.cellItemButton.hidden = YES;
        }
        if ([imageLocalFilePath isEqualToString:@""]) {
            [cell.cellitemImage setImage:[UIImage imageNamed:@"item01.png"]];
        }else {
            [cell.cellitemImage setImage:[UIImage imageWithContentsOfFile:imageLocalFilePath]];
        }        
        cell.cellItemName.text = [NSString stringWithFormat:@"%@",[tempItemNameArray objectAtIndex:indexPath.row]]; 

        for (UIProgressView *prog in cell.contentView.subviews) {
            if ([prog isKindOfClass:[UIProgressView class]]){
                if (prog.progress == 1) {
                    [prog removeFromSuperview];
                    cell.cellItemButton.hidden = NO;
                    DataBaseClass *itemObj = [appDelegate.itemArray objectAtIndex:indexPath.row];
                    NSString *imageLocalFilePath = nil;
                    if ([[tempitemStatusArray objectAtIndex:indexPath.row] isEqualToString:@"Available"]){
                        cell.cellItemButton.userInteractionEnabled = YES;
                        cell.userInteractionEnabled = YES;
                        cell.cellItemProgress.hidden = YES;
                        [cell.cellItemButton setTitle:@"" forState:UIControlStateNormal];
                        [cell.cellItemButton setBackgroundImage:[UIImage imageNamed:@"available_bttn_img_normal"] forState:UIControlStateNormal];
                        [cell.cellItemButton setBackgroundImage:[UIImage imageNamed:@"available_bttn_img_pressed"] forState:UIControlStateHighlighted];
                        [cell.cellItemButton addTarget:self action:@selector(confirmationAlert:) forControlEvents:UIControlEventTouchUpInside];
                    }else if ([[tempitemStatusArray objectAtIndex:indexPath.row] isEqualToString:@"Active"]) {
                        cell.cellItemButton.userInteractionEnabled = YES;
                        cell.userInteractionEnabled = YES;
                        cell.cellItemProgress.hidden = YES;
                        [cell.cellItemButton setTitle:@"" forState:UIControlStateNormal];
                        [cell.cellItemButton setBackgroundImage:[UIImage imageNamed:@"active_bttn_img_normal"] forState:UIControlStateNormal];
                        [cell.cellItemButton setBackgroundImage:[UIImage imageNamed:@"active_bttn_img_normal"] forState:UIControlStateHighlighted];
                        [cell.cellItemButton addTarget:self action:@selector(alert) forControlEvents:UIControlEventTouchUpInside];
                    }

                    imageLocalFilePath = [NSString stringWithFormat:@"%@",itemObj.availableLocalIconPath];
                    if ([imageLocalFilePath isEqualToString:@""]) {
                        [cell.cellitemImage setImage:[UIImage imageNamed:@"item01.png"]];
                    }else {
                        [cell.cellitemImage setImage:[UIImage imageWithContentsOfFile:imageLocalFilePath]];
                    }
                    cell.cellItemName.text = [NSString stringWithFormat:@"%@",[tempItemNameArray objectAtIndex:indexPath.row]];

                    [cell.contentView reloadInputViews];
                }
            }
        }

    }else {
        for (UIProgressView *prog in cell.contentView.subviews) {
            if ([prog isKindOfClass:[UIProgressView class]]){
                if (prog.progress == 1) {
                    [prog removeFromSuperview];
                    cell.cellItemButton.hidden = NO;
                    cell.cellItemButton.userInteractionEnabled = YES;
                    cell.userInteractionEnabled = YES;
                    [cell.cellItemButton setTitle:@"" forState:UIControlStateNormal];
                    [cell.cellItemButton setBackgroundImage:[UIImage imageNamed:@"active_bttn_img_normal"] forState:UIControlStateNormal];
                    [cell.cellItemButton setBackgroundImage:[UIImage imageNamed:@"active_bttn_img_normal"] forState:UIControlStateHighlighted];
                    [cell.cellItemButton removeTarget:nil action:NULL forControlEvents:UIControlEventAllEvents];
                    [cell.cellItemButton addTarget:self action:@selector(alert) forControlEvents:UIControlEventTouchUpInside];
                }
            }
        }        
    }

    return cell;
}
Mithuzz
  • 1,091
  • 14
  • 43
  • You never release your cell, which accounts for the leak. When you return the cell from your getCellContentView method, call autorelease on it. I'd also like to point out that you've organized your code in such a way that defeats the purpose of recycling cells. – Jason Coco Sep 03 '12 at 11:59
  • what does the `getCellContentView` method do exactly? – Malloc Sep 03 '12 at 12:02
  • I tried autorelease as Darren said, but I am still having the leak. And as part of this imageview and label I am also using a progressview in this cell. And I need to keep the progress even if it navigate to another view. – Mithuzz Sep 03 '12 at 12:04
  • @Malloc this is to set the objects for the custom cell. – Mithuzz Sep 03 '12 at 12:05

3 Answers3

4

In your method

- (UIMenuItemCell *) getCellContentView:(NSString *)cellIdentifier

Use:

UIMenuItemCell *cell = [[[UIMenuItemCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier] autorelease];

Edited to respond to comments:

The proper way to release the iVars is in the dealloc method. If it's not getting called, that means your UIMenuItemCells aren't being released. Possibly this is because of the way that your implementation works; you're creating a reusable cell for every row in your table view. I think that if you mark a cell for reuse it doesn't get released until the table view is released.

You could test this by seeing if all the memory is released when your table view is released (if this happens in your app). That would mean that you don't actually have a leak, just that your app uses a lot of memory (most likely unnecessarily). You could also test to see if the memory increases when you scroll rows that you've already created back onto the screen, because in that case I think you will be reusing your cells, so the memory shouldn't increase. You could also try initializing your table view cells with nil for the cell identifier so that they get released when they scroll off screen.

You should probably try to rework your code though so that you do actually reuse your cells. Otherwise scrolling is most likely going to be pretty choppy.

Darren
  • 10,091
  • 18
  • 65
  • 108
  • @john If you're autoreleasing the cell, there is no longer a leak. If you still see one, then it means you didn't post your actual, cut-and-paste code. If you don't post your actual code, we can't help you. – Jason Coco Sep 03 '12 at 12:08
  • thanks for your reply, I have updated the code, in addition to that, I need to keep the progress bar progress even after some navigations. Thats why I afraid to release the entire cell. I tried the autorelease as you said, but no effect. Please check the code. – Mithuzz Sep 03 '12 at 12:12
  • @John Are you releasing your synthesized iVars in your UIMenuItemCell dealloc method? – Darren Sep 03 '12 at 12:18
  • No, but I had added -(void)dealloc method in UIMenuItemCell.m and tried to release the iVars, but that method wasn't called. Is there any proper way to release the objects in a subclass? – Mithuzz Sep 03 '12 at 12:21
0

I meet similar issue, and finally resolve it.

because your table cell would be reused later,

so just call removeFromSuperview before add subviews to the cell.

andrewchan2022
  • 4,953
  • 45
  • 48
0

I had a memory spike issue while loading image to imageView. below code fixed my issue,

Note: This will reduce the image quality. In my case i'm loading image in smaller image view.

        let resizedImage = image.aspectFittedToHeight(100)
        resizedImage.jpegData(compressionQuality: 0.2)
        return resizedImage
    }

and add this Extension

extension UIImage {
    
    func aspectFittedToHeight(_ newHeight: CGFloat) -> UIImage {
        let scale = newHeight / self.size.height
        let newWidth = self.size.width * scale
        let newSize = CGSize(width: newWidth, height: newHeight)
        let renderer = UIGraphicsImageRenderer(size: newSize)
        return renderer.image { _ in
            self.draw(in: CGRect(origin: .zero, size: newSize))
        }
    }
}
Sasinderan N
  • 77
  • 1
  • 3