【发布时间】:2012-10-10 21:13:41
【问题描述】:
[使事情简明扼要]
我写了一个自定义的转场。
-(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。
当单击destinationViewController 中触发另一个segue 的按钮时,应用程序会崩溃。
我尝试删除[preV removeFromSuperview],但无济于事。
[详情]
我最近开始使用 Object-C,并编写了一个自定义 segue 来模拟 push segue。
第一次触发时,一切正常。
但是在那之后,无论触发什么segue,
应用程序崩溃,我会收到 EXC_BAD_ACCESS 错误。
我的第一个猜测是这与内存管理有关。有些东西必须被释放,但我不知道它是什么。
我的第二个猜测是,这与 UIView 和 UIWindow 提供的基础设施有关。但是再一次,由于我缺乏知识和经验,我无法弄清楚真正的问题是什么。
我知道我实际上可以采取一种简单的方法并通过使用 rootviewcontroller 并简单地隐藏导航栏来创建推送转场,但我真的很想知道我看似结构良好的自定义转场到底有什么问题并学习代码结构下面发生了什么。
[更新]
感谢 Phillip Mills 和 Joachim Isaksson 在进行一些实验并使用断点和 Zombie 工具后提出的建议,
这是我意识到的:
自定义 segue 被按钮触发后,只有当下一个 segue 也被按钮触发时,应用才会崩溃。使用
viewDidAppear触发下一个 segue 不会导致任何崩溃。崩溃的主要原因:
一个 Objective-C 消息被发送到一个被释放的对象(僵尸)
[#, 事件类型, 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 以某种方式触发了释放对象的操作,在单击触发另一个 segue 的按钮(在 destinationViewController 中)后,将向该对象发送一条 Objective-C 消息。
更多详情
没有一个prepareForSegue 被调用,因为我不需要在视图之间传递数据。
我的 segues 都是以相同的方式触发的:
- (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,所以我自己并没有真正执行任何释放操作..
[更新 2]
变成僵尸的对象是自定义segue的destinationViewController。
在自定义 segue 中不调用 removeFromSuperview 并不能阻止对象变成僵尸。
只要我使用常规模型转场或推送转场(使用 rootViewController)而不是我制作的自定义转场,就不会有任何僵尸,一切都会正常工作。
【问题讨论】:
-
查看崩溃的详细信息可能很有用。如果您在 Objective-C 异常上设置断点(使用 Breakpoint Navigator),它可能会提供更好的信息。崩溃点的堆栈回溯也可能会给出一些提示。
-
Zombie tool in Instruments 对这个程序有什么要说的吗?
-
感谢您的建议。 @JoachimIsaksson 僵尸工具最有帮助!这是一个伟大的发现,我刚刚更新了问题:)
-
@Arch 我认为需要一些代码......因为 segues 使它崩溃,并且第一次崩溃后的任何 segue,第一个 prepareForSegue 将是我的主要嫌疑人。
-
@JoachimIsaksson 但我根本没有使用
prepareForSegue,因为我不需要在视图之间传递数据。我的segues都很简单。它们由[self performSegueWithIdentifier:@"segue" sender:self]触发。
标签: iphone objective-c runtime-error uistoryboardsegue data-management