模拟推送的定制segue将VC变成僵尸



[使事情简短而清晰]

我已经写了一个自定义segue。

-(void)perform {
UIView *preV = ((UIViewController *)self.sourceViewController).view;
UIView *newV = ((UIViewController *)self.destinationViewController).view;
[preV.window insertSubview:newV aboveSubview:preV];
newV.center = CGPointMake(preV.center.x + preV.frame.size.width, newV.center.y);
[UIView animateWithDuration:0.4
 animations:^{
     newV.center = CGPointMake(preV.center.x, newV.center.y);
     preV.center = CGPointMake(0- preV.center.x, newV.center.y);}
    completion:^(BOOL finished){ [preV removeFromSuperview]; }];
}

触发segue时,也不例外。但是,它将 DealLocate destinationViewController

当按钮触发另一个segue时,该应用会崩溃,单击destinationViewController

我尝试删除[preV removeFromSuperview],但无济于事。

[详细信息]

我最近开始使用object-c,我写了一个自定义segue来模拟推动。

第一次触发它,一切正常。

但是,无论触发哪些segue,应用程序崩溃,我会收到EXC_BAD_ACCESS错误。


我的第一个猜测是这与内存管理有关。那里的某些东西必须划分,但我不知道这是什么。

我的第二个猜测是,这与UIViewUIWindow提供的基础结构有关。但是再次,由于我缺乏知识和经验,我无法弄清楚什么是真正的问题。

我知道我实际上可以采用一种简单的方法,并通过使用rootviewcontroller并只是隐藏导航栏,但我真的很想知道我看似构建精良的自定义Segue到底出了什么问题并了解什么是在代码的结构下进行。


[更新]

感谢Phillip Mills&在进行了一些实验并利用断点和僵尸工具之后

这就是我意识到的:

  1. 通过按钮触发自定义SEGUE之后,只有在下一个按钮也触发下一个SEGUE时,该应用才会崩溃。使用viewDidAppear触发下一个segue不会导致任何崩溃。

  2. 崩溃背后的主要原因:

将Objective-C消息发送到Deleclocated对象(Zombie)

[#,事件类型,refct,库,呼叫者]

0   Malloc  1   UIKit   -[UIClassSwapper initWithCoder:]
1   Retain  2   UIKit   -[UIRuntimeConnection initWithCoder:]
2   Retain  3   UIKit   -[UIRuntimeConnection initWithCoder:]
3   Retain  4   UIKit   -[UIRuntimeConnection initWithCoder:]
4   Retain  5   UIKit   -[UIRuntimeConnection initWithCoder:]
5   Retain  6   UIKit   -[UIRuntimeConnection initWithCoder:]
6   Retain  7   UIKit   -[UIRuntimeConnection initWithCoder:]
7   Retain  8   UIKit   -[UIRuntimeConnection initWithCoder:]
8   Retain  9   UIKit   UINibDecoderDecodeObjectForValue
9   Retain  10  UIKit   UINibDecoderDecodeObjectForValue
10  Retain  11  UIKit   -[UIStoryboardScene setSceneViewController:]
11  Retain  12  UIKit   -[UINib instantiateWithOwner:options:]
12  Release 11  UIKit   -[UINibDecoder finishDecoding]
13  Release 10  UIKit   -[UINibDecoder finishDecoding]
14  Release 9   UIKit   -[UIRuntimeConnection dealloc]
15  Release 8   UIKit   -[UIRuntimeConnection dealloc]
16  Release 7   UIKit   -[UIRuntimeConnection dealloc]
17  Release 6   UIKit   -[UIRuntimeConnection dealloc]
18  Release 5   UIKit   -[UINibDecoder finishDecoding]
19  Release 4   UIKit   -[UIRuntimeConnection dealloc]
20  Release 3   UIKit   -[UIRuntimeConnection dealloc]
21  Release 2   UIKit   -[UIRuntimeConnection dealloc]
22  Retain  3   UIKit   -[UIStoryboardSegue initWithIdentifier:source:destination:]
23  Retain  4   ProjectX    -[pushlike perform]
24  Retain  5   UIKit   -[UINib instantiateWithOwner:options:]
25  Retain  6   UIKit   +[UIProxyObject addMappingFromIdentifier:toObject:forCoder:]
26  Retain  7   UIKit   -[UIProxyObject initWithCoder:]
27  Retain  8   UIKit   -[UIRuntimeConnection initWithCoder:]
28  Retain  9   UIKit   UINibDecoderDecodeObjectForValue
29  Retain  10  UIKit   UINibDecoderDecodeObjectForValue
30  Release 9   UIKit   -[UINib instantiateWithOwner:options:]
31  Release 8   UIKit   +[UIProxyObject removeMappingsForCoder:]
32  Release 7   UIKit   -[UINibDecoder finishDecoding]
33  Release 6   UIKit   -[UIRuntimeConnection dealloc]
34  Release 5   UIKit   -[UINibDecoder finishDecoding]
35  Release 4   UIKit   -[UINibDecoder finishDecoding]
36  Release 3   ProjectX    -[pushlike perform]
37  Retain  4   libsystem_sim_blocks.dylib  _Block_object_assign
38  Retain  5   UIKit   -[UIApplication _addAfterCACommitBlockForViewController:]
39  Release 4   UIKit   -[UIStoryboardSegue dealloc]
40  Release 3   UIKit   _UIApplicationHandleEvent
41  Release 2   UIKit   -[UIStoryboardScene dealloc]
42  Retain  3   UIKit   _applyBlockToCFArrayCopiedToStack
43  Release 2   UIKit   _applyBlockToCFArrayCopiedToStack
44  Release 1   UIKit   __destroy_helper_block_739
45  Release 0   UIKit   _applyBlockToCFArrayCopiedToStack
46  Zombie  -1  UIKit   -[UIApplication sendAction:to:from:forEvent:]

这意味着(如果我没有错)

自定义SEGUE以某种方式触发了交易对象的内容,在按钮(在destinationViewController中)将触发另一个SEGUE的objective-c消息发送到该对象。


更多详细信息

没有一个prepareForSegue,因为我不需要在视图之间传递数据。

我的曲目都以相同的方式触发:

- (void)viewDidLoad
{
    [super viewDidLoad];
    CGRect buttonFrame = CGRectMake( 10, 40, 200, 50 );
    UIButton *button = [[UIButton alloc] initWithFrame: buttonFrame];
    [button setTitle: @"Go" forState: UIControlStateNormal];
    [button addTarget:self action:@selector(nextView) forControlEvents:UIControlEventTouchUpInside];
    [button setTitleColor: [UIColor blackColor] forState: UIControlStateNormal];
    [self.view addSubview:button]; 
}
- (void)nextView{
    [self performSegueWithIdentifier:@"push" sender:self];
}

我启用了弧线,所以我自己并没有自己做任何任何交易。


[更新2]

已变成僵尸的对象是自定义SEGUE的destinationViewController

在自定义SEGUE中未调用removeFromSuperview不会阻止对象变成僵尸。

只要我使用常规的模型segue或推送segue(带有rootviewController),而不是我制作的自定义一个,就不会有任何僵尸,一切都可以正常工作。

您只是因为执行后未保留新的控制器而崩溃。

您要做的是:

  • SRC和DEST控制器是实例化的
  • 您执行动画
  • 完成时,您删除SRC视图
  • 您的SRC控制器将发布,但是Window的rootViewController still 指向它,您的目标视图控制器不添加到Window的层次结构中。

这将按照预期的方式工作:

-(void)perform {
  UIView *preV = ((UIViewController *)self.sourceViewController).view;
  UIView *newV = ((UIViewController *)self.destinationViewController).view;
  UIWindow *window = [[[UIApplication sharedApplication] delegate] window];
  newV.center = CGPointMake(preV.center.x + preV.frame.size.width, newV.center.y);
  [window insertSubview:newV aboveSubview:preV];
  [UIView animateWithDuration:0.4
                   animations:^{
                       newV.center = CGPointMake(preV.center.x, newV.center.y);
                       preV.center = CGPointMake(0- preV.center.x, newV.center.y);}
                   completion:^(BOOL finished){
                       [preV removeFromSuperview];
                       window.rootViewController = self.destinationViewController;
                   }];
}

在iOS中,viewcontroller与.view是特殊的关系。

所有这些运行时调用(initWithCoder:UIStoryboardSegue initWithIdentifier:source:destination:等)都指出试图访问与幕后的ViewController有关的某些内容,而您所做的一件事是将其删除的。预期在其监督中。

通过在sourceViewController的.View上执行removeFromSuperview,您正在邀请破坏。

如果您想要一个不是推segue的视图,则可以使Segue成为模态segue。这将使您不得不弄乱导航栏,但可以允许Cocoatouch运行时间为您完成SEGUE(以及后来清理)的工作。

如果您真的希望控制权留在同一ViewController中,则可以修改代码以执行[preV setHidden:YES][preV setAlpha:0]。它仍然会在那里,因此它不会变成僵尸,您可以通过逆转以上两个动作中的哪个。

来返回它。

您甚至可以尝试将呼叫删除(或评论呼叫)到[reprom removerfromsuperview],然后将其从NewV中返回的任何事情时将其退回。

编辑

要考虑的另一件事是在可变preV上使用__ block存储类型,因为您在本地声明它,并且由于您要离开范围,并且原始的ViewController可能会导致它消失。在完成块中,您最终可能会引用一个已将其ref-count降至0的变量的引用,并在到达那里时已删除。 __block旨在防止这种情况。

您需要对destinationViewController进行参考,以免被交易。显然,您不能在自定义segue中执行此操作,因为过渡完成后,此对象被释放。标准推segue通过将视图控制器添加到UITabBarController的 CC_23属性。

在您的情况下,您可以致电

[sourceViewController addChildViewController:destinationViewController];

在视图控制器之间建立正确的连接。不要忘记在您的反向选择中致电removeFromParentViewController

最新更新