一个模拟推送Segue的自定义Segue将VC变成Zombie [英] a Custom Segue that Simulates a Push Segue turns VC into Zombie
问题描述
我写了一个自定义的segue。
I've written a custom 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
。
When the segue is triggered there is no exception. However it would deallocate destinationViewController
.
当点击 destinationViewController
中触发另一个segue的按钮时,应用程序会崩溃。
The app would crash when a button, that triggers another segue, in destinationViewController
is clicked.
我尝试删除 [preV removeFromSuperview]
但无济于事。
I tried removing [preV removeFromSuperview]
but to no avail.
我最近开始使用Object-C,并编写了一个模拟推送segue的自定义segue。
I've recently started working with Object-C and I wrote a custom segue that simulates a push segue.
第一次被触发时,一切正常。
The first time it is been triggered, everything works fine.
但在那之后,无论触发了什么segue,
应用程序崩溃,我会收到 EXC_BAD_ACCESS
错误。
But after that, no matter what segue is been triggered,
the app crashes and I would receive a EXC_BAD_ACCESS
error.
我的第一个猜测是这与内存管理有关。那里的东西必须要解除分配,但我不知道它是什么。
My first guess is that this has to do with memory management. Something out there has to be deallocated but I have no idea what it is.
我的第二个猜测是,这与 UIView
和 UIWindow
。但是再一次,由于我缺乏知识和经验,我无法弄清楚真正的问题是什么。
My second guess is that this has to do with the infrastructure provided by UIView
and UIWindow
. But once again, due to my lack of knowledge and experience, I can't figure out what the real problem is.
我知道我实际上可以采取一种简单的方法通过使用 rootviewcontroller
创建一个推送segue并简单地隐藏导航栏,但我真的想知道我看似构造良好的自定义segue究竟出了什么问题,并了解什么是在代码结构下面进行。
I know I can actually take an easy approach and create a push segue by using a rootviewcontroller
and simply hiding the navigation bar, but I really want to know what exactly is wrong with my seemingly well-constructed custom segue and learn what is going on underneath the fabric of codes.
感谢Phillip Mills& Joachim Isaksson的建议,在进行了一些实验并利用断点和僵尸工具之后,
Thank Phillip Mills & Joachim Isaksson for the suggestions, after conducting some experiments and making use of breakpoints and the Zombie tool,
这是我意识到的:
-
在按钮触发自定义segue后,应用程序只会在按钮触发下一个segue时崩溃。使用
viewDidAppear
触发下一个segue不会导致任何崩溃。
After the custom segue has been triggered by a button,the app would only crash when the next segue is also triggered by a button. Triggering the next segue using
viewDidAppear
would not lead to any crash.
崩溃背后的主要原因:
将Objective-C邮件发送到已解除分配的对象(僵尸)
[#,事件类型,refCt,图书馆,来电者]
[#, event type, refCt, Library, Caller]
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
中)点击触发另一个segue。
the custom segue has somehow triggered something that deallocates objects, to which an objective-C message would be sent after button (in destinationViewController
) that triggers another segue is clicked.
由于我不需要在视图之间传递数据,因此没有调用 prepareForSegue
。
Not a single prepareForSegue
is been called since I don't need to pass data between views.
我的segues都以相同的方式触发:
My segues are all triggered the same way:
- (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,所以我没有真正做任何解除分配我自己..
I have my ARC enabled so I didn't really do any of the deallocation myself..
[更新2]
已经变成僵尸的对象是自定义segue的 destinationViewController
。
The object that has been turned into a zombie is the destinationViewController
of the custom segue.
在自定义segue中不调用 removeFromSuperview
不会阻止对象变成僵尸。
Not calling removeFromSuperview
in the custom segue does not stop the object from turning into zombie.
只要我使用一个普通的模型segue或push segue(用rootViewController)而不是我自己制作的那个,不会有任何僵尸,一切都会正常工作。
As long as I use a regular model segue or a push segue (with rootViewController) instead the custom one I made, there won't be any zombie and everything would work fine.
推荐答案
你的崩溃只是因为你的新控制器在segue执行后没有保留。
You're getting a crash just because your new controller not retained after segue execution.
你做的是:
- src和dest控制器被实例化
- 你执行你的动画
- in完成删除src视图
- 哟你的src控制器被释放了,但是窗口的
rootViewController
仍然指向它并且你的目标视图控制器不被添加到窗口的层次结构中。
- src and dest controllers are instantiated
- you perform your animations
- in completion you remove src view
- your src controller gets released, but window's
rootViewController
still pointing to it and your destination view controller not added to window's hierarchy.
这将按预期工作:
-(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;
}];
}
这篇关于一个模拟推送Segue的自定义Segue将VC变成Zombie的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!