按照自定义阙可模拟按如下VC变成僵尸(a Custom Segue that Simulates a

2019-08-01 16:24发布

[为了使事情简短清晰]

我写了一个自定义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被触发,没有例外。 但是它会取消分配 destinationViewController

一个按钮,触发另一SEGUE中,当应用程序将崩溃destinationViewController被点击。

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

[细节]

我最近开始使用对象-C工作,我写了模拟推赛格瑞定制SEGUE。

第一次被触发了,一切工作正常。

但在那之后,无论是被触发什么SEGUE,应用程序崩溃,我会收到EXC_BAD_ACCESS错误。


我的第一个猜测是,这与内存管理的事情。 出来的东西,必须有被释放,但我不知道它是什么。

我的第二个猜测是,这与所提供的基础设施做UIViewUIWindow 。 但是再一次,由于我缺乏知识和经验,我找不出真正的问题是什么。

我知道,我其实可以采取一个简单的方法,并通过使用创建推送赛格瑞rootviewcontroller ,只是隐藏导航栏,但我真的想知道究竟是什么毛病我貌似构建良好的定制SEGUE,了解什么是下面怎么回事织物代码。


[更新]

感谢菲利普·米尔斯和约阿希姆伊萨克松的建议,进行了一些实验,并利用断点和僵尸工具后,

这就是我认识到:

  1. 自定义SEGUE已经通过按钮触发后,当下SEGUE也由按钮触发的应用程序只会崩溃。 触发使用下一个SEGUE viewDidAppear不会导致任何崩溃。

  2. 飞机坠毁的主要原因:

一个Objective-C消息被发送到一个对象解除分配(僵尸)

[#,事件类型,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莫名其妙引发东西,重新分配的对象,到一个Objective-C消息将按钮(在后发送destinationViewController触发另一赛格瑞点击)。


更多细节

不是一个单一的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];
}

我使我的ARC所以我真的没有做任何重新分配的自己..


[UPDATES 2]

这已经变成僵尸的对象是destinationViewController定制SEGUE的。

不调用removeFromSuperview自定义SEGUE不会变成僵尸停止对象。

只要我使用普通模式赛格瑞或推赛格瑞(与RootViewController的),而不是定制一个我做了,不会有任何僵尸,一切都将正常工作。

Answer 1:

你得到只是因为你的新的控制器不执行赛格瑞后保留崩溃。

你做的是什么:

  • SRC和dest控制器被实例化
  • 你执行你的动画
  • 在完成后,您删除SRC视图
  • 你的src控制器得到释放,但窗口的rootViewController 仍然指向它和你的目的地视图控制器添加到窗口的层次。

如预期这将只是工作:

-(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;
                   }];
}


Answer 2:

在iOS上,一个的viewController之间的关系,它的.view是特殊的。

所有这些运行时调用( initWithCoder: UIStoryboardSegue initWithIdentifier:source:destination:等)指向一个尝试访问事做幕后的viewController,并有一件事你要做的就是去除它的主要.view从它预计将在它的父。

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

如果你想有一个观点,即不是推SEGUE,可以使SEGUE一个模式SEGUE。 这将让你不必乱用导航栏,但可以允许CocoaTouch运行时为你做SEGUE的工作(事后清理)。

如果你真的想控制留在相同的viewController,那么你可以修改代码来执行[preV setHidden:YES][preV setAlpha:0] 它依然存在,因此不会变成僵尸,并且可以通过反向无论你喜欢上面的两个动作取回它。

你甚至可能只是尝试删除(或注释掉调用)[上一页removeFromSuperview],当你从任何你在做newV返回滑回来。

编辑

另一个要考虑的是使用的__block存储类型的变量preV ,因为你是在本地声明它,因为你离开的范围和原来的viewController可能会导致它去了。 在完成块,你可以使用到一个已经有它的引用计数下降到0,并通过你到达那里的时候取出一个变量的引用而告终。 __block是为了防止这一点。



Answer 3:

你需要一个参考保持到destinationViewController所以它不会得到释放。 很显然,你不能在您的自定义赛格瑞作为过渡完成后该对象被释放做到这一点。 标准推赛格瑞通过将视图控制器向执行此viewControllers一个的属性UITabBarController ,例如。

在你的情况,你可以调用

[sourceViewController addChildViewController:destinationViewController];

建立视图控制器之间的适当连接。 不要忘了打电话removeFromParentViewController在反向SEGUE。



文章来源: a Custom Segue that Simulates a Push Segue turns VC into Zombie