[使事情简短而清晰]
我已经写了一个自定义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
错误。
我的第一个猜测是这与内存管理有关。那里的某些东西必须划分,但我不知道这是什么。
我的第二个猜测是,这与UIView
和UIWindow
提供的基础结构有关。但是再次,由于我缺乏知识和经验,我无法弄清楚什么是真正的问题。
我知道我实际上可以采用一种简单的方法,并通过使用rootviewcontroller
并只是隐藏导航栏,但我真的很想知道我看似构建精良的自定义Segue到底出了什么问题并了解什么是在代码的结构下进行。
[更新]
感谢Phillip Mills&在进行了一些实验并利用断点和僵尸工具之后
这就是我意识到的:
通过按钮触发自定义SEGUE之后,只有在下一个按钮也触发下一个SEGUE时,该应用才会崩溃。使用
viewDidAppear
触发下一个segue不会导致任何崩溃。崩溃背后的主要原因:
将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
。