删除子视图然后再次添加它的问题

时间:2011-05-02 09:26:48

标签: iphone objective-c cocoa-touch uiview

我在删除子视图方面遇到了问题,或者更确切地说是在删除子视图后检查它是否仍然存在。

我的应用首先在self.view中添加了一个子视图。

[self.view addSubview:tabsClippedView];

然后它为这个子视图添加了另一个子视图(它将几个按钮添加为子视图,但我想这在这个上下文中并不重要):

[tabsClippedView addSubview:tabsView];

最后,我有一个方法允许删除tabsView然后再次创建。我需要这样做,以便更新该tabsView中的按钮数量(因为用户可以删除按钮)。该方法基本上看起来像这样:

[self.tabsView removeFromSuperview];

之后我调用了一个名为showTabs的方法(我已经在应用程序的最开头调用了这个方法来添加子视图)。这就是问题所在以及我的应用程序崩溃的地方(我在调试控制台中没有错误,所以我真的不知道问题是什么......):

    if ([tabsClippedView isDescendantOfView:self.view]) {
    NSLog(@"There is already a tabsClippedView.");
} else {
    NSLog(@"There is no tabsClippedView. I'll add one...");
    [self initTabsClippedView];
}

这是应用程序崩溃的地方:当试图评估tabsView isDescendantOfView时(我没有得到以下任何日志):

if ([tabsView isDescendantOfView:tabsClippedView]) {
    NSLog(@"There is already a tabsView");
} else {
    NSLog(@"There is no tabsView for the buttons. I'll add one including buttons.");
    [self initTabs];
}

如果问题出现任何建议,我将不胜感激。


修改

这些是设置我的观点的方法:

-(void) initTabsClippedView { // sets up tabsClippedView

NSLog(@"initTabsClippedView method started...");

CGRect tabsClippedFrame = CGRectMake(258,30,70,81*6);
tabsClippedView = [[[UIView alloc] initWithFrame:tabsClippedFrame] autorelease];
tabsClippedView.clipsToBounds = true;

[self.view addSubview:tabsClippedView];

NSLog(@"initTabsClippedView method ended.");

}

    -(void) initTabs {          

    NSLog(@"initTabs started. Adding buttons to tabsClippedView...");

    CGRect tabsFrame = CGRectMake(-30,0,50,480);
    tabsView = [[[UIView alloc] initWithFrame:tabsFrame] autorelease];
    [tabsClippedView addSubview:tabsView];  
// sets up buttons in tabsClippedView

这是我删除tabsClippedView的地方(由tabsClippedView中的按钮触发):

 -(void)tabDelete:(id)sender
{
    UIButton *button = (UIButton *)sender;

    [UIView animateWithDuration:0.75
                          delay:0
                        options:UIViewAnimationOptionCurveEaseIn
                     animations:^{

                                    button.transform = CGAffineTransformMakeTranslation(-30, 0);                 

                     }
                     completion:^(BOOL finished){


                         [UIView animateWithDuration:0
                                               delay:0
                                             options:UIViewAnimationOptionCurveEaseIn
                                          animations:^{

                                              [self.tabsView removeFromSuperview];

                                              //...                                           
                                          }
                                          completion:^(BOOL finished){

                                              NSLog(@"tabsView removed from Superview. Objects Deleted.");
                                              [self showTabs]; 
                                              NSLog(@"TabDelete finished. Button removed and tabsView updated accordingly.");

                                          }
                          ];    
                     }];

这是我启动应用程序时已经调用的showTabs方法:

- (void)showTabs {

NSLog(@"showTabs started...");

currentView = @"Tabs";

if ([tabsClippedView isDescendantOfView:self.view]) {
    NSLog(@"There is already a tabsClippedView.");
} else {
    NSLog(@"There is no tabsClippedView. I'll add one...");
    [self initTabsClippedView];
}

if ([tabsView isDescendantOfView:tabsClippedView]) {
    NSLog(@"There is already a tabsView");
} else {
    NSLog(@"There is no tabsView for the buttons. I'll add one including buttons.");
    [self initTabs];
}

2 个答案:

答案 0 :(得分:1)

你有可能获得EXC_BAD_ACCESS吗?应用程序是否可能崩溃,因为tabsView在向其发送isDescendantOfView:时已取消分配。如果在启用断点的情况下运行,它应该告诉您崩溃的原因。如果是EXC_BAD_ACCESS问题,您应该尝试NSZombie

要激活NSZombie,请执行以下操作:

  1. 获取可执行文件的信息。
  2. 转到参数选项卡。
  3. 在“要在环境中设置的变量:”部分添加:
  4. 名称:NSZombieEnabled 价值:是

    然后像往常一样运行您的应用程序,当它崩溃时,它应该告诉您哪个已解除分配的对象收到了什么消息。

    编辑:刚看到你的编辑。我想我已经钉了它。您在创建视图时会自动释放视图,因此当从超级视图中删除视图时,它们将不再保留并因此被取消分配。您的应用程序崩溃是因为您尝试在解除分配的视图上运行方法。

    编辑2:我想我应该告诉你,有一个比Praveen S发布的更好的解决方案。

    按如下方式更改您的代码:

    [tabsClippedView release];
    tabsClippedView = [[UIView alloc] initWithFrame:tabsClippedFrame];
    

    [tabsView release];
    tabsView = [[UIView alloc] initWithFrame:tabsFrame];
    

    上面的代码与Praveen S发布的代码完全相同,但没有自动释放。自动释放比常规版本更昂贵,并且只应在需要时使用,在这种情况下不应该使用。

    在分配新视图之前,您可能希望在完成视图后释放视图,而不是发布:

    [tabsView removeFromSuperview];
    [tabsView release];
    tabsView = nil;
    

    或只是

    [tabsView removeFromSuperview];
    self.tabsView = nil;
    

    然后代替:

    if ([tabsView isDescendantOfView:tabsClippedView]) ...
    

    你可以使用:

    if (tabsView) ...
    

    您可能已经注意到,确实没有必要保留视图。您也可以执行以下操作:

    tabsView = [[UIView alloc] initWithFrame:tabsFrame];
    [tabsClippedView addSubview:tabsView]; // This retains tabsView
    [tabsView release];
    

    然后删除您要使用的视图:

    [tabsView removeFromSuperview]; // This will release the tabsView
    tabsView = nil;
    

    另请记住在viewDidUnload中将视图设置为nil。

    编辑3:为什么自己造成这样的差异:

    您需要了解的是属性和引用计数的工作原理。有书,你可以阅读它。我相信Google也可以为您提供一些很好的参考资料。

    之间的区别
    self.tabsView = [[UIView alloc] initWithFrame:frame];
    

    tabsView = [[UIView alloc] initWithFrame:frame];
    

    self.tabsView正在访问属性setter,而tabsView正在直接访问实例变量。

    非原子,retain属性的实现类似于以下内容:

    - (void)setTabsView:(UIView *)view
    {
        if (view != tabsView) {
            [tabsView release];
            tabsView = [view retain];
        }
    }
    

    因此,该物业正在为您处理内存管理。在我的解决方案中,我自己处理内存管理,因此我不需要属性为我做,所以我不使用它。

    我希望这能解释为什么自我造成这样的差异。

答案 1 :(得分:1)

按如下方式更改您的代码:

self.tabsClippedView = [[[UIView alloc] initWithFrame:tabsClippedFrame] autorelease];

self.tabsView = [[[UIView alloc] initWithFrame:tabsFrame] autorelease];