删除子视图然后再次添加时出现问题

发布于 2024-11-04 11:32:02 字数 4001 浏览 0 评论 0原文

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

我的应用程序首先向 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.");

                                          }
                          ];    
                     }];

这是当我删除 tabsClippedView 时已经调用的 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];
}

I have a problem with removing a subview, or more precisely with checking if it is still there after having deleted it.

My app first adds a subview to self.view.

[self.view addSubview:tabsClippedView];

Then it adds another subview to this subview (to which it adds several buttons as subviews, but I guess this is unimportant in this context):

[tabsClippedView addSubview:tabsView];

Finally, I have a method which allows the tabsView to be deleted and then created again. I need to do this so as to update the number of buttons in that tabsView (as the user can delete buttons). The method looks basically like this:

[self.tabsView removeFromSuperview];

After that I call a method called showTabs (which I already called in the very beginning of the app in order to add the subViews). This is where it all becomes problematic and where my app crashes (I get no error in the debug console, so I don't really know what the issue is...):

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

This is where the app crashes: when trying to assess if tabsView isDescendantOfView (I don't get any of the following logs):

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];
}

I'd be grateful for any suggestions where the problem could be.


EDIT:

These are the methods to set up my views:

-(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

And this is where I delete the tabsClippedView (triggered by a button found in 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.");

                                          }
                          ];    
                     }];

And this is the showTabs method which was already called when I started the app:

-(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];
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

蝶…霜飞 2024-11-11 11:32:02

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

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

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

名称:NSZombieEnabled
值:YES

然后照常运行您的应用程序,当它崩溃时,它应该告诉您哪个已释放的对象收到了什么消息。

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

编辑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:为什么 self 会产生如此大的影响:

您需要了解的是属性和引用计数是如何工作的。有一些书籍之类的你可以阅读。我相信 Google 也可以为您提供一些很好的参考。

之间的区别

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

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

在于 self.tabsView 访问属性设置器,而 tabsView 直接访问实例变量。

非原子的保留属性的实现如下所示:

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

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

我希望这能解释为什么 self 会产生如此大的影响。

Is it possible that you are getting EXC_BAD_ACCESS? Is it possible that the app is crashing because tabsView is deallocated when you send isDescendantOfView: to it. If you run with breakpoints enabled it should tell you the reason for the crash. If it is an EXC_BAD_ACCESS problem you should try NSZombie.

To activate NSZombie do the following:

  1. Get info of the executable.
  2. Go to the arguments tab.
  3. In the "Variables to be set in the environment:" section add:

Name: NSZombieEnabled
Value: YES

Then run your app as usual and when it crashes it should tell you which deallocated object received what message.

EDIT: Just saw your edit. I think I nailed it. You're autoreleasing the views when you create them, so when they are removed from their superviews they are no longer retained and thus deallocated. You're app crashes because you're trying to run methods on deallocated views.

EDIT 2: Thought I should tell you that there is a better solution than the one posted by Praveen S.

Change your code as follows:

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

and

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

The above code does the same thing as the code posted by Praveen S, but without the autorelease. An autorelease is more expensive than a regular release and should only be used when needed and in this case it isn't.

Rather than releasing before you allocate a new view you probably want to release the view when you're done with it:

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

or simply

[tabsView removeFromSuperview];
self.tabsView = nil;

and then instead of:

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

you can use:

if (tabsView) ...

As you might have noticed, there really is no need for you to retain the view. You could just as well do the following:

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

and then to remove the view you would use:

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

Also remember to set the views to nil in viewDidUnload.

EDIT 3: Why self made such a difference:

What you need to understand is how properties and reference counting works. There are books and such you could read about it. I'm sure Google can provide you with some good references as well.

The difference between

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

and

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

is that self.tabsView is accessing the properties setter, while tabsView is accessing the instance variable directly.

A nonatomic, retain property's implementation looks something like the following:

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

So the property is taking care of the memory management for you. In my solution I take care of the memory management myself and thus I don't need the property to do it for me, so I don't use it.

I hope this explains why self made such a difference.

彩扇题诗 2024-11-11 11:32:02

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

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

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

Change your code as follows:

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

and

self.tabsView = [[[UIView alloc] initWithFrame:tabsFrame] autorelease];
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文