我在删除子视图方面遇到了问题,或者更确切地说是在删除子视图后检查它是否仍然存在。
我的应用首先在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];
}
答案 0 :(得分:1)
你有可能获得EXC_BAD_ACCESS
吗?应用程序是否可能崩溃,因为tabsView
在向其发送isDescendantOfView:
时已取消分配。如果在启用断点的情况下运行,它应该告诉您崩溃的原因。如果是EXC_BAD_ACCESS
问题,您应该尝试NSZombie
。
要激活NSZombie,请执行以下操作:
名称: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];