UIView removeFromSuperview 导致应用程序崩溃

Posted

技术标签:

【中文标题】UIView removeFromSuperview 导致应用程序崩溃【英文标题】:UIView removeFromSuperview causes app to crash 【发布时间】:2013-02-11 11:20:12 【问题描述】:

让我解释一下我的问题。我有 3 个UIView:一个 LoginViewLibraryView 和一个 StoreView。我有这段代码可以从一个 UIView 切换到另一个:

- (void)showView:(NSInteger)viewTag

  if (viewTag == 1)
  
      if (self.loginView)
      
        self.loginView = nil;
        self.loginView.delegate = nil;
      

      LoginView *loginPage = [[LoginView alloc]initWithFrame:self.view.bounds];
      [loginPage setDelegate:self];

      self.loginView = loginPage;

      [loginPage release];

      [self.view addSubview:self.loginView];

else if(viewTag == 2)

    if (self.libraryView)
    
        self.libraryView = nil;
        self.libraryView.delegate = nil;
    

    LibraryView *libraryPage = [[LibraryView alloc]initWithFrame:self.view.bounds];
    [libraryPage setDelegate:self];

    self.libraryView = libraryPage;

    [libraryPage release];

    [self.view addSubview:self.libraryView];

else

    if (self.bookStoreView)
    
        self.bookStoreView = nil;
        self.bookStoreView.delegate = nil;
    

    BookStoreView *bookStore = [[BookStoreView alloc]initWithFrame:self.view.bounds];
    [bookStore setDelegate:self];

    self.bookStoreView = bookStore;

    [bookStore release];

    [self.view addSubview:self.bookStoreView];

基本上,这就是我初始化 UIView 的方式。以下是用于在它们之间切换的按钮:

- (void)loginViewToLibraryView
  
     [self.loginView removeFromSuperview];
     [self showView:2];
  

- (void)libraryViewToStoreView
  
     [self.libraryView removeFromSuperview];
     [self showView:3];
  

  //so on...

当我调用函数libraryViewToLoginViewstoreViewToLoginView 时出现问题。每当我调用这些函数时,应用程序就会崩溃,这很奇怪,因为这两个函数之前都可以正常工作。我检查了个人资料,它给了我这个堆栈跟踪:

#   Address     Category Event RefCt  Timestamp    Size Responsible Library   Responsible Caller
0   0xc4dcac0   CALayer Malloc  1   00:02.233.004   48  UIKit                 -[UIView _createLayerWithFrame:]
1   0xc4dcac0   CALayer Retain  3   00:02.238.317   0   QuartzCore             CA::Layer::insert_sublayer(CA::Transaction*, CALayer*, unsigned long)
2   0xc4dcac0   CALayer Release 2   00:02.238.324   0   UIKit                 -[UIView(Internal) _addSubview:positioned:relativeTo:]
3   0xc4dcac0   CALayer Retain  3   00:02.238.518   0   QuartzCore            -[CALayerArray copyWithZone:]
4   0xc4dcac0   CALayer Release 2   00:02.238.602   0   UIKit                 -[UIView(Hierarchy) _makeSubtreePerformSelector:withObject:withObject:copySublayers:]
5   0xc4dcac0   CALayer Retain  3   00:02.238.665   0   QuartzCore            -[CALayerArray copyWithZone:]
6   0xc4dcac0   CALayer Release 2   00:02.238.796   0   UIKit                 -[UIView(Internal) _didMoveFromWindow:toWindow:]
7   0xc4dcac0   CALayer Retain  3   00:05.107.397   0   QuartzCore            -[CALayerArray copyWithZone:]
8   0xc4dcac0   CALayer Release 2   00:05.107.539   0   UIKit                 -[UIView(Hierarchy) _makeSubtreePerformSelector:withObject:withObject:copySublayers:]
9   0xc4dcac0   CALayer Retain  3   00:05.107.613   0   QuartzCore            -[CALayerArray copyWithZone:]
10  0xc4dcac0   CALayer Release 2   00:05.107.700   0   UIKit                 -[UIView(Internal) _didMoveFromWindow:toWindow:]
11  0xc4dcac0   CALayer Retain  2   00:06.105.958   0   QuartzCore            -[CALayerArray copyWithZone:]
12  0xc4dcac0   CALayer Release 2   00:06.108.134   0   UIKit                 -[UIView dealloc]
13  0xc4dcac0   CALayer Release 1   00:06.108.492   0   UIKit                 -[UIView dealloc]
14  0xc4dcac0   CALayer Zombie  -1  00:06.115.332   0   QuartzCore            CA::release_objects(X::List<void const*>*)

如您所见,这是我不太了解的 CALayer 上的一堆调用。我想了解为什么会这样。谁能解释一下?

【问题讨论】:

你有一个僵尸进程 = 你正在释放释放的对象。尝试使用 Instruments 来分析内存泄漏。向我们展示 loginView 和 libraryView 的@property,它们是否强大/保留? 您是否尝试在将最近创建的视图添加为子视图后释放它们?我对 ARC 之前的 obj c 不是很熟悉,但是,afaik 向视图添加子视图会创建新的强引用,所以也许你可以交换这些行(和类似的行):[bookStore release]; [self.view addSubview:self.bookStoreView]; 我只是想知道loginViewlibraryViewbookStoreView 是否具有保留属性? 所有提到的视图都是保留属性。我现在将交换 Hermann 在答案部分中提到的代码,看看它是否有帮助。 【参考方案1】:

坦率地说,我还没有理解您想要在这里实现的所有目标。但是你应该考虑事件的顺序。查看我的 cmets:

  if (self.loginView)
  
    self.loginView = nil;
    //self.loginView is nil now. What so you think doese happen on the next line? 
    self.loginView.delegate = nil;
    // change the sequence of this lines and it will be ok. 
  

  LoginView *loginPage = [[LoginView alloc]initWithFrame:self.view.bounds];
  [loginPage setDelegate:self];

  self.loginView = loginPage;

  [loginPage release]; //here you release the object. it is gone now. However, there are still references to it. 

  [self.view addSubview:self.loginView]; // here you add the released object. What do you expect to happen? 
  // Switch those two statmetns and you should be fine. 

嗯,它甚至可以工作,因为这些 statmen 彼此相邻。当你从它的超级视图中删除它时,它会再次被释放。你的应用程序可能会崩溃。

顺便说一句,如果不保留它,您根本不应该释放它。还是我在这里错了?但是, addSubview 应该保留它,而 removeFromSuperview 将释放它,因此不需要额外的释放。一次删除它应该消失了。 (如果没有保留在其他地方)

【讨论】:

感谢您的回复!不过老实说,我并没有完全理解它。我从另一个程序员那里继承了它,我仍在学习流程中。所以谢谢你帮助我!如果可行,我会接受你的回答。 好的。那是旧代码吗?这就是为什么你不使用自动引用计数?对于新项目,您应该考虑使用它。很多由内存管理引起的痛苦都消失了。 实际上是的。我尝试使用 Xcode 中的 Refactor 将它转换为 ARC,但它说有些东西阻止它转换。我没有太多时间去寻找那些只是为了转换为 ARC 的东西,所以我决定暂时保留它。 我还有一个问题。 loginPage 在发布之前被分配给self.loginView,然后self.loginView 被添加为子视图。 self.loginView 不应该保留loginPage 吗? 我从未尝试过,因为我会使用相反的顺序。当保留计数为 1 或更多时,首先释放然后添加为子视图肯定会起作用(意思是:当它同时被其他代码保留时)。我猜想即使保留计数为 0,它仍然可以工作,因为它被添加为子视图并在发布后直接保留在那里。但我不会打赌。

以上是关于UIView removeFromSuperview 导致应用程序崩溃的主要内容,如果未能解决你的问题,请参考以下文章

如果其他 UIView 带有隐藏选项,如何使下 UIView 固定到 UIScrollView 中的上 UIView?

在 UIView2 上方显示 UIView1,但在 UIView1 上方显示 UIView2 输入方式

统计从多个 UIView 中随机抽出的 UIView 数量,即:从 9 个 UIView 中随机抽出 5 个 UIView,然后执行操作

UIView 类别 - 自定义方法不返回 UIView

IOS将UIVIew中的UIImageView扁平化为单个UIView [重复]

UIView - 啥覆盖将允许更新 UIView 框架