0

I am writing an application where the user should be able to click on a button and have the views switch. I have created IBActions that load my method switchSubView on the desired view, but have been having some issues in doing so. I have currently written methods to animate the transition between two NSViews connected through Interface Builder as IBOutlets. I am able to use the methods to transition between two views, but when I try to switch from the initial view to another view, then switch to another, and then switch back, I get EXC_BAD_ACCESS. I can't seem to debug the problem. The debugger shows both of the NSView objects to point to distinct memory locations. To clarify, I would like to reuse the views throughout the duration of the application's execution. I would not like any of the views to be deallocated.

Here is the code:

- (void)switchSubViews:(NSView *)firstView withView:(NSView *)secondView {
    [firstView setSubviews:[NSArray array]];
    [self prepareViews:firstView];
    [[firstView animator] addSubview:secondView];
}

/* Make a "move" animation effect. */
- (void)prepareViews:(NSView *)prepareView {
    CATransition * transition = [CATransition animation];
    [transition setType:kCATransitionPush];
    [transition setSubtype:kCATransitionFromRight];
    [prepareView setAnimations:[NSDictionary dictionaryWithObject:transition forKey:@"subviews"]];
    [prepareView setWantsLayer:YES];
}

Basically, I would call it like this, given initialView and newView:

[self switchSubViews:initialView withView:newView];

Can anyone help me debug this?

Thanks.

Update: After commenting out the for loop and the removeFromSuperview code to debug, I am still receiving the bad access error. The properties for the view are set to @property (nonatomic, strong), so shouldn't the views be retained? If not, how can I explicitly retain my NSView(s) with ARC enabled? Is it possible to tell ARC to retain it, or is it already retaining the view since it is set with @property(nonatomic, strong)?

Second Update: I researched a little and found out that I can cause ARC to retain the object by creating a strong pointer to the object. I tried it by creating instance variables:

@interface AppDelegate : NSObject <NSApplicationDelegate, NSWindowDelegate> {
    /* Some instance variables here... */
    __strong NSView * retainFirstView;
    __strong NSView * retainSecondView;
}

@property (nonatomic, strong) NSView * retainFirstView;
@property (nonatomic, strong) NSView * retainSecondView;

And then in the .m file:

#import <Quartz/Quartz.h> /* Animation. */
#import "AppDelegate.h"
/* Some other imports necessary... */

@implementation AppDelegate

@synthesize retainFirstView;
@synthesize retainSecondView; 

/* Some subview switching code... */
[self setRetainFirstView:firstView];
[self setRetainSecondView:secondView];

@end

but I still receive the error. It seems that the view that I am swapping out is consistently being deallocated. I've spent significant time on this and haven't been able to get rid of the error. Any help would be appreciated. If this isn't the proper way to retain the view, please let me know.

Update 3: To test, I have disabled ARC temporarily and did this before calling setSubviews or addSubview:

[firstView retain]; 
[secondView retain];

and it still failed. I am quite confused, as I no longer see what could be causing the deallocation.

Update 4: I checked for zombies and messages sent to deallocated objects by doing Product -> Profile in Xcode, and to my surprise, I wasn't notified of any. I am very confused on why this is occurring.

ra1nmaster
  • 662
  • 1
  • 8
  • 21

1 Answers1

4

You're getting EXC_BAD_ACCESS because calling -removeFromSuperview will deallocate that view if nothing else is retaining it. It's very likely that a view that is being deallocated (therefore having an object pointer that points to garbage) is later passed into -switchSubviews:withView, causing your crash. You should always retain views that you are going to be using later on before calling -removeFromSuperview to make sure that they are not deallocated.

As a sidenote, the code for -switchSubviews:withView: is needlessly complicated. It could be reduced to this:

- (void)switchSubViews:(NSView *)firstView withView:(NSView *)secondView {
    firstView.subviews = [NSArray array];
    [self prepareViews:firstView];
    [[firstView animator] addSubview:secondView];
}
indragie
  • 18,002
  • 16
  • 95
  • 164
  • Thanks for that, I probably would never have figured that out myself. But I still have a slight problem. How do I retain a view if my project is using ARC? I'm not allowed to explicitly call the retain message. – ra1nmaster Dec 24 '13 at 13:37
  • It seems that even after commenting out the removeFromSuperview code to debug, the error persists. This means the view is being deallocated, but I can't find out from where. I set the view properties with property (nonatomic, strong) so doesn't this mean that the views should be retained by ARC? – ra1nmaster Dec 24 '13 at 17:03
  • So should I retain every subview of firstView? If so, how could I do that? – ra1nmaster Dec 24 '13 at 22:56
  • You should retain any subview that you're planning on using later. Under ARC you don't explicitly send `-retain` and `-release` messages but it still supports storage specifiers like `strong` and `weak` to manage references in the object graph. For example, you could store the subviews in an array and assign them to a property declared like this: `@property (strong) NSArray *viewArray` – indragie Dec 24 '13 at 23:38
  • Thanks so much! I managed to get it to work using your suggestion with the NSArray * viewArray. – ra1nmaster Dec 25 '13 at 01:29