4

There have been a few questions on StackOverflow where users have experienced the same problem as I am having. However, none of their solutions fit my case. (See here, here, here and here for some of the SO questions I've read but have not found helpful.)

In my case, I have a NIB that has a couple UIButtons, with an associated Controller View. The view is relatively old to my project and I've been able to use these buttons without any trouble until today. After making a few code changes that were not related to the button behavior, I've run into an error that crashes the app, breaks the code at the main() function and gives me an EXC_BAD_ACCESS error message whenever I touch any of the buttons on my View.

How or why might this happen? I've actually commented out almost all functional code, especially that which I modified earlier today and I still can't stop the error from occurring.

My project is using Automatic Reference Counting and I haven't seen this error before. Furthermore, I did not modify the NIB, nor the IBAction associated with the buttons so I don't see what would cause this. The only way to stop the error is to unlink my UIButtons in my NIB to the IBActionmethods defined in my Controller View header file.

The only "unique" aspect of my use-case is that I load either one or two instances of this view, within another sub view controller. The number of instances of the broken view that are loaded is contingent on the number of objects in an array. Below is the code that I use to instantiate and load these views as subviews of another view.

//Called else where, this starts the process by creating a view that 
//will load the problematic view as a sub-view either once or twice.
- (id)initWithPrimarySystemView:(SystemViewController *)svc
{
    //First we create our parent, container view.
    self = [super initWithNibName:@"ContainerForViewInstaniatedFromArrayObjs" bundle:nil];
    if (self) 
    {
        //Assign parent DataModel to local instance
        [self setDataModel:((DataModelClass*)svc.DataModel)];
        for (AnotherModel* d in DataModel.ArrayOfAnotherModels)
        {
            //Instantiate the SubViewController.
            SubViewController* subsvc = [[SubViewController alloc] 
                                            initWithNibName:@"Subview" 
                                          bundle:nil 
                                          subviewPosition:d.Position ];

            //Add the SubViewControllers view to this view.
            [subsvc.view setFrame:CGRectMake((d.Position-1)*315, 0, 315, 400)];
            [self.view addSubview:subsvc.view];
        }
        [self setDefaultFrame: CGRectMake(0, 0, 640, 400)];
    }
    return self;
}

This works perfectly and, previously, hadn't even caused any trouble with the buttons that were on the associated view, however, now all UIButtons crash the app when tapped.

The initialization function for SubViewController, as well as the viewDidLoad method contain nothing other than the standard, auto-generated code that is added when you create a new ViewController.

What can I do to either fix or diagnose this problem?

Community
  • 1
  • 1
RLH
  • 15,230
  • 22
  • 98
  • 182

2 Answers2

8

See my comments in your code:

{
    SubViewController* subsvc = [[SubViewController alloc] initWithNibName:@"Subview" bundle:nil subviewPosition:d.Position ];
    //!i: By default, subsvc is a __strong pointer, so your subview has a +1 retain count
    //    subsvc owns subsvc.view, so subsvc.view has a +1 retain count as well

    //Add the SubViewControllers view to this view.
    [subsvc.view setFrame:CGRectMake((d.Position-1)*315, 0, 315, 400)];

    [self.view addSubview:subsvc.view];
    //!i: This bumps subsvc.view to +2, as self.view strong-references it

    //!i: subsvc is going out of scope, so the reference count on subsvc will drop
    //    to 0 and it is dealloc'd.  subsvc.view's retain count drops to +1, as it
    //    is still referenced by self.view
    //
    //    Most likely, in -[SubViewController dealloc], you were not doing a 
    //    setTarget:nil, setAction:nil on the button.  Thus, the button now 
    //    has a dangling pointer and will crash when hit
}

To fix this, add each SubViewController instance to an array owned by the master view controler. That will keep the SubViewController instances around to receive the button taps.

iccir
  • 5,078
  • 2
  • 22
  • 34
  • Interesting, I'll give this a try when I get to my desk. I can see why this would fix the problem but I am a bit confused-- I've had this mechanism in place for a couple weeks. I haven't had a single problem with it, nor had I tweaked and of the associated code. If what you are stating is the problem, it seems like this would have always been a problem, not just magically on one arbitrary day. Would you have any clue why this would work under certain circumstances? – RLH Feb 02 '12 at 14:03
  • Did you just recently upgrade to ARC? This looks like a leak in manual reference counting. It's also possible that you had something else retaining SubViewController and keeping it alive before - possibly the run loop if you were using timers, or UIViewAnimation if you were animating. – iccir Feb 03 '12 at 01:42
  • iccir-- Sorry for the late response. I just noticed your last question. This was a new project that I began in early December. Being that I am new to any form of Apple development, all that I have experienced is ARC development. It's convenient, but I feel like I'm missing a lot of understanding since I've never programmed under a traditional paradigm in objective-c. – RLH Feb 21 '12 at 13:41
1

Make sure in your dealloc you call:

[button removeTarget:nil action:NULL forControlEvents:UIControlEventAllEvents];

Even though you though you didn't need "dealloc's" in ARC, you do because of what iccir explained.

dacopenhagen
  • 2,414
  • 2
  • 23
  • 29
  • Is this necessary if all of these UIView objects are created in IB and connected through IBOutlets? I've never read instruction to do this. – RLH Feb 21 '12 at 15:07
  • 1
    yes - especially with IB. IB creates, but it does NOT handle the destruction of your objects. – dacopenhagen Mar 27 '12 at 19:10