4

当我在仪器中测试我的应用程序的内存泄漏时,我什么也没发现(使用模拟器运行)。但是当我在手机上运行它然后检查时,UIKit 对象中有很多泄漏。这发生在每个视图中。在模拟器中没有显示此类泄漏。

下面是发生一些泄漏的仪器的屏幕截图。

在此处输入图像描述

当我从 HomeView 移动到 secondViewController 时,没有发现泄漏。如果再次回到家,发现这些泄漏。那么,这是否意味着,我必须释放/取消我在该 secondView 中使用的所有 UI 对象。供您参考,以下是我在 secondView 中使用的 UI 对象。

1.Two Background UIImageView
2.One TitleBar UIImageView
3.3 UIButtons(Back,left and right button for iCarousel)
4.One iCarousel view
5.UIPageController(For this I have used a third Party code SMPageControl)
6.One title label. 

注意:我的是非 ARC 代码。

以前有没有人遇到过这个问题。我该如何克服这个问题,因为我在我的应用程序的每个视图中都有这个问题。因此,我的应用程序经常出现内存警告并经常崩溃。

谢谢你。

下面是该视图的我的实现文件。

编辑1:

 @implementation CatalogueViewController

@synthesize deptCarousel    = _deptCarousel;
@synthesize carouselItems   = _carouselItems;
@synthesize categorymAr     = _categorymAr;
@synthesize spacePageControl = _spacePageControl;
@synthesize wrap;

- (void)dealloc {
    _deptCarousel = nil;
    [_categorymAr               release];
    _categorymAr                = nil;
    _deptCarousel.delegate      = nil;
    _deptCarousel.dataSource    = nil;
    [_deptCarousel              release];
    [_carouselItems             release];
    [viewGesture release];
    viewGesture = nil;
    [_spacePageControl release];
    _spacePageControl = nil;
    imgViewBG = nil;
    imgViewBG2 = nil;
    btnPrev = nil;
    btnNext = nil;
//    [self releaseObjects];
    [super dealloc];
}


- ( IBAction) btnBackClicked {
    [self.navigationController popViewControllerAnimated:YES];
}

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        // Custom initialization
    }
    return self;
}

- (void)didReceiveMemoryWarning
{
    // Releases the view if it doesn't have a superview.
    [super didReceiveMemoryWarning];
    // Release any cached data, images, etc that aren't in use.
}

#pragma mark - View lifecycle

- (void)viewDidLoad
{
    [super viewDidLoad];
    self.title = NSLocalizedString(@"catalogue", @"Catalogue");

    // Do any additional setup after loading the view from its nib.

    _deptCarousel.type  = iCarouselTypeLinear;
    _deptCarousel.scrollSpeed = 0.3f;
    _deptCarousel.bounceDistance = 0.1f;
    _deptCarousel.scrollToItemBoundary = YES;
    _deptCarousel.stopAtItemBoundary = YES;
    [_deptCarousel setScrollEnabled:NO];

    UISwipeGestureRecognizer *swipeLeft = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipeNext:)];
    swipeLeft.direction = UISwipeGestureRecognizerDirectionLeft;
    [viewGesture addGestureRecognizer:swipeLeft];
    [swipeLeft release];

    UISwipeGestureRecognizer *swipeRight = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipePrev:)];
    swipeRight.direction = UISwipeGestureRecognizerDirectionRight;
    [viewGesture addGestureRecognizer:swipeRight];
    [swipeRight release];

    UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleSingleTap:)];
    [viewGesture addGestureRecognizer:singleTap];
    [singleTap release];

    _carouselItems      = [[NSMutableArray alloc] initWithCapacity:1];
    _categorymAr        = [[NSMutableArray alloc] initWithCapacity:1];
    [self addCatalogues];
    _spacePageControl.numberOfPages = [_categorymAr count];
    [_spacePageControl setPageIndicatorImage:[UIImage imageNamed:IS_IPAD?@"Marker1.fw.png" : @"Markeri.png"]];
    [_spacePageControl setCurrentPageIndicatorImage:[UIImage imageNamed:IS_IPAD?@"Marker-Highlight.png" : @"Marker-Highlight_i.png"]];
    [_spacePageControl addTarget:self action:@selector(spacePageControl:) forControlEvents:UIControlEventValueChanged];

}



- (void)spacePageControl:(SMPageControl *)sender{
    [_deptCarousel scrollToItemAtIndex:sender.currentPage animated:YES];
}

- ( void ) addCatalogues {
    [_categorymAr addObjectsFromArray:[[DBModel database] categoryList]];

    for (int i = 0; i < [_categorymAr count]; i++) {
        [_carouselItems addObject:[NSNumber numberWithInt:i]];
    }
    [_deptCarousel reloadData];
}

- (void)viewDidUnload{

    [super viewDidUnload];
    // Release any retained subviews of the main view.
    // e.g. self.myOutlet = nil;
}

- (void)viewWillAppear:(BOOL)animated
{
    [self phoneType];
    [super viewWillAppear:animated];
    if (IS_IPAD) {
        UIInterfaceOrientation statusBarOrientation = [[UIApplication sharedApplication] statusBarOrientation];
        [self handleOrientation:statusBarOrientation];
    }
}

- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
 }


- ( void ) phoneType{

    if(!IS_IPAD){
        if(IS_IPHONE5){
            imgViewBG.image = [UIImage imageNamed:@"Background5_5.jpg"];
            imgViewBG.center = CGPointMake(162,265);
            imgViewBG2.image = [UIImage imageNamed:@"Background11_5.png"];
            _spacePageControl.center = CGPointMake(160, 478);
            _deptCarousel.center = CGPointMake(160, 355);
            viewGesture.center = CGPointMake(160, 355);
            btnPrev.center = CGPointMake(25, 355);
            btnNext.center = CGPointMake(295, 355);
        }
        else{
            imgViewBG.image = [UIImage imageNamed:@"Background5.jpg"];
            imgViewBG2.image = [UIImage imageNamed:@"Background9.png"];            
        }
    }

}


-(void)textFieldDidBeginEditing:(UITextField *)textField{

    textFieldSearch.placeholder = @"";
    UIButton *clearButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 20, 20)];
    [clearButton setImage:[UIImage imageNamed:IS_IPAD?@"Btn_X_Large.fw.png":@"Btn_X.fw.png"] forState:UIControlStateNormal];
    [clearButton addTarget:self action:@selector(btnClearTextField) forControlEvents:UIControlEventTouchUpInside];
    [textFieldSearch setRightViewMode:UITextFieldViewModeAlways];
    [textFieldSearch setRightView:clearButton];
    [clearButton release];

}

-(void)textFieldDidEndEditing:(UITextField *)textField{
    [textFieldSearch setRightView:nil];
    if ([textFieldSearch.text isEqualToString:@""]) {
        textFieldSearch.placeholder = NSLocalizedString(@"hud_search_for_a_product_here",@"");
    }
}

-(IBAction)btnClearTextField{
    textFieldSearch.text = @"";
}

- (NSUInteger)supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskAll;
}



- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    if (IS_IPAD) {
        return YES;
    } else {
        return (interfaceOrientation == UIInterfaceOrientationPortrait || interfaceOrientation == UIInterfaceOrientationPortraitUpsideDown);
    }
}

- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation )toInterfaceOrientation duration:(NSTimeInterval)duration{
    if (IS_IPAD) {
        [self handleOrientation:toInterfaceOrientation];
    }
}

- ( void ) handleOrientation:(UIInterfaceOrientation )toInterfaceOrientation {



    if (toInterfaceOrientation == UIInterfaceOrientationPortrait || toInterfaceOrientation == UIInterfaceOrientationPortraitUpsideDown) {
        imgViewBG.image = [UIImage imageNamed:@"Background_Catalogue_P.jpg"];
        imgViewBG2.image = [UIImage imageNamed:@"Background_Overlay_P.fw.png"];
        btnPrev.center = CGPointMake(90, 640);
        btnNext.center = CGPointMake(677, 640);
        textFieldSearch.frame = CGRectMake(187, 54, 418, 25);
        _deptCarousel.frame = CGRectMake(235, 250, 300, 800);
        _spacePageControl.center = CGPointMake(385, 920);
        viewGesture.center = CGPointMake(385, 658);

    }else {
        imgViewBG.image = [UIImage imageNamed:@"Background_Catalogue_L.jpg"];
        imgViewBG2.image = [UIImage imageNamed:@"Background_Overlay_L.fw.png"];
        btnPrev.center = CGPointMake(54, 385);
        btnNext.center = CGPointMake(640, 385);
        textFieldSearch.frame = CGRectMake(240, 55, 567, 25);
        _deptCarousel.frame = CGRectMake(50, 250, 600, 300);
        _spacePageControl.center = CGPointMake(346, 660);
        viewGesture.center = CGPointMake(347, 405);

    }
}

- ( IBAction )btnDepartmentClicked:(id)sender {
    int btnTag = [sender tag];
    ProductCategoriesViewController *productView = [[ProductCategoriesViewController alloc] initWithNibName:@"ProductCategoriesView" bundle:nil];
    if ( btnTag == 0 ) {
        [productView setStrTitle:NSLocalizedString(@"women", @"Women")];
    }else if ( btnTag == 1 ) {
        [productView setStrTitle:NSLocalizedString(@"men", @"Men")];
    } else {
        [productView setStrTitle:NSLocalizedString(@"sports", @"Sports")];
    }
    [self.navigationController pushViewController:productView animated:YES];

    [productView release];
}


- ( BOOL ) textFieldShouldReturn:( UITextField * )textField {
    [textField resignFirstResponder];
    [Flurry logEvent:@"Product searched" withParameters:[NSDictionary dictionaryWithObjectsAndKeys:textField.text,@"1", nil]];
    [self productSearch:textField.text isBar:NO isQR:NO];
    return YES;
}

- ( void ) productSearch:( NSString * )_searchText isBar:( BOOL )_isBar isQR:( BOOL )_isQr {
    if ([_searchText isEqualToString:@""]) {
        return;
    }

    NSMutableArray *ProductList = [[NSMutableArray alloc] init];
    [ProductList addObjectsFromArray:[[DBModel database] productSearch:_searchText isBar:_isBar isQR:_isQr]];
    if ( [ProductList count] == 0 ) {
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"product", @"")
                                                        message:NSLocalizedString(@"cannot_find_product", @"")
                                                       delegate:nil
                                              cancelButtonTitle:NSLocalizedString(@"ok", @"")
                                              otherButtonTitles:nil];
        [alert show];
        [alert release];        
    } else {
        GeneralProductListViewController *generalProductList = [[GeneralProductListViewController alloc] initWithNibName:IS_IPAD?@"GeneralProductListView~iPad": @"GeneralProductListView" bundle:nil];
        [generalProductList setMArProducts:ProductList];
        [self.navigationController pushViewController:generalProductList animated:YES];
        [generalProductList release];
    }
    [ProductList release];
  }


-(IBAction) spin:(id)sender {


    if([sender tag]==0)
    {

        [_deptCarousel scrollToItemAtIndex:[self.deptCarousel currentItemIndex]+1 animated:YES];
//         [_deptCarousel scrollByNumberOfItems:1 duration:2.0];
        }

    else{
     [_deptCarousel scrollToItemAtIndex:[self.deptCarousel currentItemIndex]-1 animated:YES];

    }

}

-(void)swipeNext:(UISwipeGestureRecognizer *)recognizer{

     [_deptCarousel scrollToItemAtIndex:[self.deptCarousel currentItemIndex]+1 animated:YES];

}

-(void)swipePrev:(UISwipeGestureRecognizer *)recognizer{

    [_deptCarousel scrollToItemAtIndex:[self.deptCarousel currentItemIndex]-1 animated:YES];

}

-(void) handleSingleTap:(UITapGestureRecognizer *)recognizer{

    if ([_categorymAr count] > 0) {

        ProductCategoriesViewController *prodCatView = [[ProductCategoriesViewController alloc] initWithNibName:IS_IPAD ?
                                                    @"ProductCategoriesView~iPad" : @"ProductCategoriesView" bundle:nil];
        Category *categoryObj = [_categorymAr objectAtIndex:[self.deptCarousel currentItemIndex]];
        [prodCatView setStrTitle:categoryObj.categoryName];
        [prodCatView setCategoryId:categoryObj.categoryId];
        [Flurry logEvent:@"Category List" withParameters:[NSDictionary dictionaryWithObjectsAndKeys:categoryObj.categoryName,[NSString stringWithFormat:@"%d",categoryObj.categoryId], nil]];
        [self.navigationController pushViewController:prodCatView animated:YES];
        [prodCatView release];
    }
}
//-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
//    pageControl.currentPage = [self.deptCarousel currentItemIndex] ;
//}

#pragma mark
#pragma mark NavigationBarViewDelegate metho

- ( void ) navigationBackClicked {
    [self.navigationController popViewControllerAnimated:YES];
}

#pragma mark -
#pragma mark iCarousel methods

- (NSUInteger)numberOfItemsInCarousel:(iCarousel *)carousel
{
    return [_carouselItems count];
}

- (NSUInteger)numberOfVisibleItemsInCarousel:(iCarousel *)carousel
{
    //limit the number of items views loaded concurrently (for performance reasons)
    return NUMBER_OF_VISIBLE_ITEMS;
}

- (UIView *)carousel:(iCarousel *)carousel viewForItemAtIndex:(NSUInteger)index
{
    Category *categoryObj = [_categorymAr objectAtIndex:index];
    //create a numbered view
    UIView *view = nil;
    NSString *imagePath = [[APP_CACHES_DIR stringByAppendingPathComponent:@"catalogues"] stringByAppendingString:[NSString stringWithFormat:@"/%d.jpg", categoryObj.categoryId]];
    if (![[NSFileManager defaultManager] fileExistsAtPath:imagePath]) {
        view = [[[UIImageView alloc] initWithImage:[UIImage imageNamed:IS_IPAD?@"Gallery Placeholder.png":@"Gallery Placeholder.png"]] autorelease];
    } else {
        view = [[[UIImageView alloc] initWithImage:[UIImage imageWithContentsOfFile:[[APP_CACHES_DIR stringByAppendingPathComponent:@"catalogues"] stringByAppendingString:[NSString stringWithFormat:@"/%d.jpg", categoryObj.categoryId]]]] autorelease];
    }



    if (IS_IPAD) {
        view.frame = CGRectMake(0, 0, 420, 420);
    } else {
        view.frame = CGRectMake(0, 0, 200, 200);
    }

//  UILabel *label = [[[UILabel alloc] initWithFrame:CGRectMake(view.bounds.origin.x, view.bounds.origin.y+view.bounds.size.height, view.bounds.size.width, 44)] autorelease];
//  label.text = categoryObj.categoryName;
//    label.textColor = [UIColor blackColor];
//  label.backgroundColor = [UIColor clearColor];
//  label.textAlignment = UITextAlignmentCenter;
//    label.font = [UIFont fontWithName:@"Helvetica-Bold" size:IS_IPAD?26:14];
//  [view addSubview:label];


    return view;
}

- (NSUInteger)numberOfPlaceholdersInCarousel:(iCarousel *)carousel
{
    //note: placeholder views are only displayed on some carousels if wrapping is disabled
    return INCLUDE_PLACEHOLDERS? 2: 0;
}

- (UIView *)carousel:(iCarousel *)carousel placeholderViewAtIndex:(NSUInteger)index
{
    //create a placeholder view
    UIView *view = [[[UIImageView alloc] initWithImage:[UIImage imageNamed:@""]] autorelease];
    UILabel *label = [[[UILabel alloc] initWithFrame:view.bounds] autorelease];
    label.text = (index == 0)? @"[": @"]";
    label.backgroundColor = [UIColor clearColor];
    label.textAlignment = UITextAlignmentCenter;
    label.font = [label.font fontWithSize:50];

    _spacePageControl.currentPage = index;


//  [view addSubview:label];
    return view;
}

- (CGFloat)carouselItemWidth:(iCarousel *)carousel
{
    //usually this should be slightly wider than the item views
    return ITEM_SPACING;
}

- (CATransform3D)carousel:(iCarousel *)_carousel transformForItemView:(UIView *)view withOffset:(CGFloat)offset
{
    //implement 'flip3D' style carousel

    //set opacity based on distance from camera
    view.alpha = 1.0 - fminf(fmaxf(offset, 0.0), 1.0);

    //do 3d transform
    CATransform3D transform = CATransform3DIdentity;
    transform.m34 = _deptCarousel.perspective;
    transform = CATransform3DRotate(transform, M_PI / 8.0, 0, 1.0, 0);

    return CATransform3DTranslate(transform, 0.0, 0.0, offset * _deptCarousel.itemWidth);
}

- (BOOL)carouselShouldWrap:(iCarousel *)carousel
{
    //wrap all carousels
//    return NO;
    return wrap;
}

- (void)carousel:(iCarousel *)carousel didSelectItemAtIndex:(NSInteger)index {
    if (index == [self.deptCarousel currentItemIndex]) {
        ProductCategoriesViewController *prodCatView = [[ProductCategoriesViewController alloc] initWithNibName:IS_IPAD ? 
                                                        @"ProductCategoriesView~iPad" : @"ProductCategoriesView" bundle:nil];
        Category *categoryObj = [_categorymAr objectAtIndex:index];
        [prodCatView setStrTitle:categoryObj.categoryName];
        [prodCatView setCategoryId:categoryObj.categoryId];
        [Flurry logEvent:@"Category List" withParameters:[NSDictionary dictionaryWithObjectsAndKeys:categoryObj.categoryName,[NSString stringWithFormat:@"%d",categoryObj.categoryId], nil]];
        [self.navigationController pushViewController:prodCatView animated:YES];
        [prodCatView release];
    }
}

-(void) carouselDidScroll:(iCarousel *)carousel{

//    [_deptCarousel scrollToItemAtIndex:[self.deptCarousel currentItemIndex]+3 animated:YES];

//    [_deptCarousel scrollByNumberOfItems:1 duration:1];

}

- (void)carouselCurrentItemIndexUpdated:(iCarousel *)carousel{

    _spacePageControl.currentPage = [self.deptCarousel currentItemIndex];
}

- ( IBAction ) myCart {
    if ( [[DBModel database] isShoppingListEmpty] ) {
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"at_shopping_cart", @"") 
                                                        message:NSLocalizedString(@"amsg_shopping_cart_empty", @"")
                                                       delegate:nil cancelButtonTitle:NSLocalizedString(@"ok", @"") otherButtonTitles:nil];
        [alert show];
        [alert release];
        return;
    }
    MyCartViewController *myCartView = [[MyCartViewController alloc] initWithNibName:IS_IPAD ? @"MyCartView~iPad" : @"MyCartView" bundle:nil];
    [self.navigationController pushViewController:myCartView animated:YES];
    [myCartView release];
}
4

3 回答 3

2

首先,如前所述,使用 ARC。您可以做的任何事情都无法更好地改善内存管理。

无论您是否使用 ARC,您都应该始终使用访问器来访问您的 ivars(除了 ininitdealloc)。正如@LombaX 所指出的,您在viewDidLoad. 使用访问器会有所帮助。

您应该运行静态分析器,这将帮助您发现其他内存错误。

我怀疑你有一个IBOutlet配置为retain并且你没有在dealloc. 这是我在您的屏幕截图中看到的泄漏的最可能原因。ARC 通常会使此类问题自动消失。

您很可能有一个保留循环。这通常不会显示为泄漏。您应该使用heapshot进行调查。您的泄漏很小;它们可能不是内存警告的实际原因。您想要调查的(使用分配工具)实际上显着增加了您的内存使用量。

但首先是ARC。然后是访问器。然后删除所有构建警告。然后删除所有静态分析器警告。然后使用分配工具。

旁注:它说责任方是“UIKit”的事实并不意味着这是 UIKit 中的错误。这只是意味着 UIKit 分配了后来泄漏的内存。泄漏的原因可能在其他地方。(也就是说,UIKit 确实有几个小漏洞。一般来说,它们不应该给您带来麻烦,但您可能永远无法摆脱 iOS 应用程序中 100% 的小漏洞。)

于 2013-07-17T15:02:22.877 回答
1

第一的:

您可能有明显的泄漏,但我不确定它是否与您在仪器中发现的泄漏相同:

这两行在您的 viewDidLoad 方法中

_carouselItems      = [[NSMutableArray alloc] initWithCapacity:1];
_categorymAr        = [[NSMutableArray alloc] initWithCapacity:1];

但是:viewDidLoad:每次视图由其控制器加载时调用。如果控制器清除视图(例如在内存警告之后),在第二个 viewDidLoad 中,您的_carouselItems_categorymAr实例变量将丢失对先前创建的 NSMutableArray 的引用,从而导致泄漏

因此,更改这些行并使用 syntesized 设置器:

self.carouselItems      = [[[NSMutableArray alloc] initWithCapacity:1] autorelease];
self.categorymAr        = [[[NSMutableArray alloc] initWithCapacity:1] autorelease];

syntesized setter 配置为在分配新对象之前释放前一个对象。

但是:您可能还有另一个泄漏。如果您可以简单地重现泄漏(如果我理解,泄漏似乎只是从 VC 移动到另一个),您可以使用仪器的“heapshot”功能。

假设您的泄漏似乎从第一个 VC 移动到第二个并返回:

  • 使用分配工具打开工具
  • 从第一个 VC 到第二个然后回来。
  • 按左侧的“标记堆”。将出现一条线。
  • 再次从第一个 VC 到第二个并回来。
  • 再按一下“heapshot”
  • 这样做几次(9-10)

heapshot 工具会在您按下按钮时对活体对象进行“快照”,并仅向您显示差异。如果有 2-3 个新对象,您将在列表中看到它。

这是调查泄漏的良好起点。看附图:

快照分析

考虑到您必须多次标记堆并通过查看创建的对象来区分“误报”,在我的示例中,您可以发现可能的泄漏(heapshot5, 1,66KB),但在查看内容后它不是 -->这是从那一刻开始的后台任务。

此外,自动释放池的延迟和一些 UIKit 对象的缓存会在堆中显示一些东西,这就是为什么我说要多试几次。

于 2013-07-17T14:34:48.417 回答
0

检测泄漏来源的一种简单方法是使用仪器的扩展详细信息视图。

为此,单击“查看”->“扩展详细信息”,将出现带有“泄漏”堆栈跟踪的右侧菜单。在那里,您可以轻松找到每个泄漏的泄漏代码以及它们是否来自您的应用程序。

于 2013-07-17T14:14:46.710 回答