删除子视图然后再次添加时出现问题
我在删除子视图时遇到问题,或者更准确地说,在删除子视图后检查它是否仍然存在。
我的应用程序首先向 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
您是否有可能获得
EXC_BAD_ACCESS
?应用程序是否可能崩溃,因为当您向应用程序发送 isDescendantOfView: 时,tabsView 被释放。如果您在启用断点的情况下运行,它应该告诉您崩溃的原因。如果是EXC_BAD_ACCESS
问题,您应该尝试NSZombie
。要激活 NSZombie,请执行以下操作:
名称:NSZombieEnabled
值:YES
然后照常运行您的应用程序,当它崩溃时,它应该告诉您哪个已释放的对象收到了什么消息。
编辑:刚刚看到您的编辑。我想我成功了。您在创建视图时会自动释放视图,因此当它们从超级视图中删除时,它们将不再保留并因此被释放。您的应用程序崩溃是因为您尝试在已释放的视图上运行方法。
编辑2:我想我应该告诉你,有一个比 Praveen S 发布的解决方案更好的解决方案。
按如下方式更改代码:
上面的代码与
Praveen S 发布的代码执行相同的操作,但没有自动释放。自动释放比常规释放更昂贵,并且应该仅在需要时使用,但在本例中则不然。
您可能希望在完成视图后释放视图,而不是在分配新视图之前释放它:
或者简单地
然后而不是:
您可以使用:
正如您可能已经注意到的,您实际上没有必要保留的观点。您也可以执行以下操作:
然后删除您将使用的视图:
还要记住在
viewDidUnload
中将视图设置为 nil。编辑3:为什么 self 会产生如此大的影响:
您需要了解的是属性和引用计数是如何工作的。有一些书籍之类的你可以阅读。我相信 Google 也可以为您提供一些很好的参考。
之间的区别
和
在于 self.tabsView 访问属性设置器,而 tabsView 直接访问实例变量。
非原子的保留属性的实现如下所示:
因此该属性正在为您处理内存管理。在我的解决方案中,我自己负责内存管理,因此我不需要该属性为我做这件事,所以我不使用它。
我希望这能解释为什么 self 会产生如此大的影响。
Is it possible that you are getting
EXC_BAD_ACCESS
? Is it possible that the app is crashing becausetabsView
is deallocated when you sendisDescendantOfView:
to it. If you run with breakpoints enabled it should tell you the reason for the crash. If it is anEXC_BAD_ACCESS
problem you should tryNSZombie
.To activate NSZombie do the following:
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:
and
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:
or simply
and then instead of:
you can use:
As you might have noticed, there really is no need for you to retain the view. You could just as well do the following:
and then to remove the view you would use:
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
and
is that
self.tabsView
is accessing the properties setter, whiletabsView
is accessing the instance variable directly.A nonatomic, retain property's implementation looks something like the following:
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.
按如下方式更改您的代码:
和
Change your code as follows:
and