UIButton подправляет IBAction, вызывая EXC_BAD_ACCESS с ARC

На StackOverflow было несколько вопросов, когда пользователи столкнулись с той же проблемой, что и я. Однако ни одно из их решений не подходит для моего случая. (См. здесь, здесь, здесь и здесь для некоторых вопросов SO, которые я читал, но не нашел полезным.)

В моем случае у меня есть NIB с парой UIButton и связанным с ним представлением контроллера. Представление относительно старо для моего проекта, и я мог без проблем использовать эти кнопки до сегодняшнего дня. После внесения нескольких изменений в код, не связанных с поведением кнопки, я столкнулся с ошибкой, которая приводит к сбою приложения, разрыву кода в функции main() и выдаче сообщения об ошибке EXC_BAD_ACCESS всякий раз, когда я касаюсь любого кнопок на моем View.

Как или почему это может произойти? На самом деле я закомментировал почти весь функциональный код, особенно тот, который я модифицировал ранее сегодня, и я до сих пор не могу предотвратить возникновение ошибки.

В моем проекте используется автоматический подсчет ссылок, и я раньше не видел этой ошибки. Кроме того, я не модифицировал NIB и IBAction, связанные с кнопками, поэтому не понимаю, что могло бы вызвать это. Единственный способ остановить ошибку — отвязать мои UIButtons в моем NIB от IBActionmethods, определенных в моем заголовочном файле представления контроллера.

Единственный «уникальный» аспект моего варианта использования заключается в том, что я загружаю один или два экземпляра этого представления в другом подчиненном контроллере представления. Количество загружаемых экземпляров разбитого представления зависит от количества объектов в массиве. Ниже приведен код, который я использую для создания и загрузки этих представлений в качестве подвидов другого представления.

//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;
}

Это работает отлично и ранее даже не вызывало никаких проблем с кнопками, которые были в соответствующем представлении, однако теперь все UIButton вызывают сбой приложения при нажатии.

Функция инициализации для SubViewController, а также метод viewDidLoad не содержат ничего, кроме стандартного, автоматически сгенерированного кода, который добавляется при создании нового ViewController.

Что я могу сделать, чтобы исправить или диагностировать эту проблему?


person RLH    schedule 01.02.2012    source источник


Ответы (2)


Смотрите мои комментарии в вашем коде:

{
    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
}

Чтобы исправить это, добавьте каждый экземпляр SubViewController в массив, принадлежащий главному контроллеру представления. Это позволит сохранить экземпляры SubViewController для получения нажатий кнопок.

person iccir    schedule 01.02.2012
comment
Интересно, я попробую, когда доберусь до своего стола. Я понимаю, почему это решит проблему, но я немного сбит с толку - у меня есть этот механизм уже пару недель. У меня не было ни одной проблемы с этим, и я не настраивал связанный с ним код. Если то, о чем вы говорите, является проблемой, кажется, что это всегда было проблемой, а не просто волшебным образом в один произвольный день. Вы знаете, почему это сработает при определенных обстоятельствах? - person RLH; 02.02.2012
comment
Вы недавно перешли на ARC? Это похоже на утечку в ручном подсчете ссылок. Также возможно, что у вас было что-то еще, сохраняющее SubViewController и поддерживающее его раньше — возможно, цикл выполнения, если вы использовали таймеры, или UIViewAnimation, если вы анимировали. - person iccir; 03.02.2012
comment
iccir-- Извините за поздний ответ. Я только что заметил ваш последний вопрос. Это был новый проект, который я начал в начале декабря. Поскольку я новичок в любой форме разработки Apple, все, что я испытал, — это разработка ARC. Это удобно, но я чувствую, что мне не хватает понимания, так как я никогда не программировал в рамках традиционной парадигмы в Objective-C. - person RLH; 21.02.2012
comment
Вы - гений! Вы спасли мой день. - person ViruMax; 03.03.2015

Убедитесь, что в Dealloc вы звоните:

[кнопка removeTarget: nil action: NULL forControlEvents: UIControlEventAllEvents];

Несмотря на то, что вам не нужны «dealloc» в ARC, вы делаете это из-за того, что объяснил iccir.

person dacopenhagen    schedule 20.02.2012
comment
Нужно ли это, если все эти объекты UIView создаются в IB и подключаются через IBOutlets? Я никогда не читал инструкции, чтобы сделать это. - person RLH; 21.02.2012
comment
да - особенно с ИБ. IB создает, но НЕ занимается уничтожением ваших объектов. - person dacopenhagen; 27.03.2012