iOS 7.1 removeFromSuperview 崩溃

Posted

技术标签:

【中文标题】iOS 7.1 removeFromSuperview 崩溃【英文标题】:iOS 7.1 removeFromSuperview crash 【发布时间】:2014-03-11 17:10:22 【问题描述】:

iOS 7.1 发布之前,我的应用没有任何崩溃。现在在任何removeFromSuperview 方法上,崩溃。例如:我有视图控制器,当我想删除一个视图控制器时,我删除它的所有子视图,然后从堆栈中删除(堆栈:我将视图控制器存储在其中,用于加载新内容并加载以前的内容):

    for (UIView *subView in [contentVc subviews])
         [subView removeFromSuperview];

我得到了

-[CALayer retain]:消息发送到释放的实例

消息

[actual removeFromParentViewController];

有什么好办法去掉吗?它会释放整个视图控制器及其子视图吗?因为我的应用程序没有崩溃,而不是 removeFromSuperview。我不明白 ios 7.1 中发生了什么变化。

我如何在没有removeFromSuperview 的情况下删除viewController 中的所有子视图,并且不删除我的ViewController(如果我只想添加新的子视图并删除当前内容)?

更新:

有时会崩溃:

[myactualviewcontroller.view removeFromSuperview];

-[CALayer retain]:消息发送到释放的实例

为什么???

有时如果我尝试从视图控制器视图中删除主子视图,它会发生同样的崩溃:

[mainView removeFromSuperview](mainView是单个UIView,添加到vc.view中)

UPDATE2:(非常详细)

所以,我有一个容器视图。我在这个容器中添加了一个UIViewController.view。我正在向UIViewController.view 添加一个视图作为子视图。这个视图不是本地 uiview,我的意思是,它声明为 implementation UIView* mainView 。当我的 UIViewController 将被释放时,在其 - (void) dealloc [mainView removeFromSuperview]; [mainView release] [super dealloc]; 在 mainView removeFromSuperview 我的应用崩溃了。

【问题讨论】:

我用同样的方式发布子视图,iOS7.1更新没有破坏。也许检查subView 是否为零? (尽管这在技术上应该不是问题) 潜在的麻烦:contentVc 听起来像一个视图控制器(来自后缀),但你把它当作一个视图。这是故意的吗?那是什么? 除此之外,这段代码很好;您是否曾经对视图进行过弱或不安全的引用? 如果子视图在子视图数组中,子视图怎么可能为零。 (但关闭子视图不是零,我检查了它。) 为什么在移除视图控制器的时候感觉需要移除所有的子视图?完全没有必要。 【参考方案1】:

在快速枚举数组时修改数组通常不是一个好主意。您似乎在视图的子视图数组上使用快速枚举,并同时修改该数组(通过删除子视图)。你可以试试这样的:

NSArray *subviewsCopy = [[contentVc subviews] copy];
for (UIView *subview in subviewsCopy) 
    [subview removeFromSuperview];

但是,正如其他人所提到的,您需要费心手动删除这些子视图,这有点奇怪。在正常情况下,当视图控制器本身被释放时,视图控制器的视图(及其下的视图层次结构)将被自动清理。

还有一些很好的工具可以帮助您找出问题的根源。特别是,您应该分析您的应用程序(在 Xcode 中,在 Product 菜单下)并在 Instruments 提示您时选择 Zombies 工具。使用 Zombies,您可以查看对象在释放后收到的消息的保留/释放历史记录。

如果您因为怀疑视图会被泄露而尝试手动清理视图层次结构,我建议您也尝试使用 Instruments 中的 Leaks 工具,并验证当禁用此代码时相关视图实际上是泄露了。

【讨论】:

我的子视图不是零,也不是 NSZombies。但是,如果我尝试将其从 superview 中删除,它会崩溃【参考方案2】:

你的程序崩溃了,因为你不止一次地发布了一些东西。这部分很明显。

找到它的第一步是在调试器中启用僵尸检测。 (Project->Schemes->Edit Scheme->Diagnostics->Enable Zombie Objects)。这里的目标是让你的程序更快地崩溃。一旦您尝试访问已释放的实例,这将使您进入调试器。有时这会为您指明正确的方向,有时则不然,但最好在尽可能靠近问题的地方检测到它。

下一步是使用 Zombies 乐器。该工具将提供比上一步更多的信息,但使用起来更复杂(这就是我将其设为第 2 步而不是第 1 步的原因)。 Zombies 工具将跟踪您的所有分配和释放,并检测您何时尝试访问僵尸对象。

最后的手段是开始注释掉代码。首先注释掉你的程序在你创建视图控制器(崩溃的那个)和你释放它之间所做的一切。然后运行程序并做任何你需要做的事情来让它显示坏的视图控制器。显然,它不会做任何事情,因为它现在只是一个空视图控制器,但它不应该崩溃)。然后开始取消注释代码块,一次一点,并在每次迭代之间继续运行它。这是一个重复的过程,如果您的视图控制器代码大而复杂,可能会很乏味。但是这个想法是继续一点一点地添加你的代码,直到你添加一些东西并且它崩溃 - 然后你知道你已经找到了导致问题的代码片段。您必须在这里发挥创造力并仔细选择将代码放回原处的方式-如果您的程序具有良好的模块化设计,那么您应该可以轻松做到这一点。意大利面条式代码很难做到这一点,但它可能会给你一个很好的机会来重组你的代码。通过这个过程,您将缩小问题范围,并最终通过消除过程找到错误。

【讨论】:

非常感谢您的回答,但正如我在评论中所写的那样,我已经解决了他们的问题。我为它使用了相同的机制:“我实现了一个自定义容器,并解决了我的随机崩溃问题,启用 nszombie 并使用工具。问题是某些东西被发布了两次,但可悲的是,Xcode 没有告诉我在哪里和什么。并且崩溃不是直接在第二个版本运行之后。它在完全随机几秒钟后崩溃。现在它工作正常。谢谢你的回答“ 我投票赞成你的答案,唯一的一件事是为什么我没有删除我的问题:它可能对某人有用:)【参考方案3】:

更新

尝试这样做:

NSArray *subviews = [NSArray arrayWithArray:[contentVc subviews]];
for (UIView *subView in subviews)
     [subView removeFromSuperview];

我认为您遇到了崩溃,因为您试图快速枚举具有可变长度的数组(实际上,当您删除子视图时,它也会从子视图数组中删除)。

如果你想删除视图控制器,只需调用:

[contentVc.view removeFromSuperView];
[contentVc removeFromParentViewController];

【讨论】:

你的意思是NSArray *subviews = [[contentVc subviews] copy];?否则没有区别【参考方案4】:

有时会崩溃:

[myactualviewcontroller.view removeFromSuperview];

您不应手动从视图层次结构中添加或删除控制器的视图,而应依赖UIWindowrootViewController,将控制器推送到UINavigationController 等,以使系统添加私有底层superviews的视图。除非你创建了一个Custom Container View Controller,我猜你不是。

如果您只想手动处理视图,请不要使用视图控制器,因为它们不会被系统保留,也不会收到任何旋转消息等。所以在这种情况下使用视图控制器无论如何都是没有意义的.

至于子视图内存处理,子视图由它们的父视图保留,所以只要不保留strong引用,就不需要释放子视图,移除一个普通的父视图即可。

同样,如果您正确使用视图控制器,只需释放控制器即可摆脱所有视图。

最后,您应该开始使用 ARC。

【讨论】:

我实现了一个自定义容器,并通过启用 nszombie 和使用工具解决了我的随机崩溃问题。问题是一些东西被发布了两次,但可悲的是,Xcode 没有告诉我在哪里和什么。并且崩溃不是直接在第二个版本运行之后发生的。它在几秒钟后完全随机崩溃。现在它工作正常。谢谢你的回答 而且我认为我不应该使用 ARC,ARC 的作用与您保留、释放相同,它只是计数而不是您。但我确切地知道我不需要对象的具体点,我可以释放。我可以编写有效的代码。 ARC决定对象引用计数是否为0,并释放。我也是这样做的,但是一个对象会一直存在,直到它有一个强引用(retain)或者直到大括号结束,但是我知道在几行代码之后我不需要这个对象。 ARC 的功能不仅仅是为您释放对象,它还支持自动取消 weak 引用,从而避免 unsafe_unretained 引用。在大多数情况下,它也会比你更有效率,对于其余的,如果需要,你可以使用@autoreleasepool【参考方案5】:

1.根据Apple's documentation,调用removeFromSuperview会将该视图从superview中移除并自动释放。

所以如果你使用removeFromSuperview,那么你不应该调用[removedView release],这会导致你的App崩溃。

请参阅 Apple 的此屏幕截图。

在你的 dealloc 实现中,你是这样的

- (void) dealloc 

    // Removed from Parent view and released.
    [mainView removeFromSuperview];

    // no mainView exists in memory , so it crashed the App.
    [mainView release];// Comment this line to avoid the crash

    [super dealloc];

2.您不应将正在枚举的容器静音。

You are having like this,

for (UIView *subView in [contentVc subviews])
     [subView removeFromSuperview];

相反,您可以通过 Apple 的这一行来实现相同的效果。

[[contentVc subviews] makeObjectsPerformSelector:@selector(removeFromSuperview)];

【讨论】:

我必须在 removeFromSuperview 之后重新设置 mainView,因为它是 viewController 的保留属性。问题不是这个。我已经解决了我的问题,我会尽快回答我的问题 这是错误的,因为 mainView 在添加到超级视图时会保留。【参考方案6】:

请确保在删除视图之前删除所有可能的代理(即[someScrollView removeFromSuperview]; 之前的someScrollViewDelegate = nil;)和/或动画已完全完成(所有CATransaction[UIViev beginAnimation...][UIView animateWithDuration...] 等)。

【讨论】:

【参考方案7】:

请执行以下操作:

1- 调试for (UIView *subView in [contentVc subviews]) 并检查它的迭代次数。

如果它在第一次点击时没有崩溃,您可以在删除视图之前添加此行 if (subView.superView != nil)

请尝试确保您不会在其他地方两次发布视图,因为它是 继续显示并且不会崩溃,直到您将其从超级视图中删除。

更新2: 我会认为你会意识到内存泄漏,并且你在这方面有很好的经验。

1234563保留计数回到一。这是诀窍:您不必释放子视图或将其从其父视图中删除以消除剩余的保留计数。您可以简单地删除或释放父视图。以 NSMutableArray 为例。

dealloc: 方法中删除[mainView removeFromSuperview];。您可以将其添加到其他位置,例如 viewWillDisappear: 方法。 dealloc 方法不应包含除释放调用之外的任何内容。

【讨论】:

最酷的是这些子视图不是零,但如果我尝试删除它会崩溃 不是子视图中的nil值,是超级视图,请尝试发消息 UIView* view = [[UIView alloc]initWithFrame: self.view.frame]; [self.view addSubview: view]; [查看发布]; for (UIView* subview in self.view.subviews) [subview removeFromSuperview]; [子视图 removeFromSuperview]; [子视图 removeFromSuperview]; ' 它没有崩溃【参考方案8】:

代替:

for (UIView *subView in subviews)
     [subView removeFromSuperview];

试试:

[subviews makeObjectsPerformSelector:@selector(@"removeFromSuperview");

【讨论】:

【参考方案9】:

尝试在 removeFromSuperview 之前先检查视图是否为 != nil 示例:@IBOutlet weak var btnSNSApple: UIView! if self.btnSNSApple != nil self.btnSNSApple.removeFromSuperview()

【讨论】:

以上是关于iOS 7.1 removeFromSuperview 崩溃的主要内容,如果未能解决你的问题,请参考以下文章

iOS 版本更新摘要iOS 7.1

iOS 6/7 Delta 不工作 iOS 7.1

在 iOS 7.1 更新中自动释放的变量崩溃,但在 iOS 7.1 之前的操作系统版本中工作正常

ios 7.1 flash 模式不工作

iOS 7.1 UITableView layoutSubviews 问题

UITextField 上的 UITapGestureRecognizer 不再适用于 IOS 7.1