[To Make things short and clear]
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]; }];
}
When the segue is triggered there is no exception. However it would deallocate destinationViewController.
The app would crash when a button, that triggers another segue, in destinationViewController is clicked.
I tried removing [preV removeFromSuperview] but to no avail.
[Details]
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.
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.
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.
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.
[Updates]
Thank Phillip Mills & Joachim Isaksson for the suggestions, after conducting some experiments and making use of breakpoints and the Zombie tool,
this is what I realise:
-
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
viewDidAppearwould not lead to any crash. -
The main reason behind the crash:
An Objective-C message was sent to a deallocated object (zombie)
[#, 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:]
which implies that (if I’m not wrong)
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.
More Details
Not a single prepareForSegue is been called since I don’t need to pass data between views.
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];
}
I have my ARC enabled so I didn’t really do any of the deallocation myself..
[UPDATES 2]
The object that has been turned into a zombie is the destinationViewController of the custom segue.
Not calling removeFromSuperview in the custom segue does not stop the object from turning into zombie.
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.
You’re getting a crash just because your new controller not retained after segue execution.
What you do is this:
rootViewControllerstill pointing to it and your destination view controller not added to window’s hierarchy.This will work just as intended: