IOS 7.1 removeFromSuperview crash

У моего приложения не было сбоев, пока не вышло iOS 7.1. Теперь на любом методе removeFromSuperview, сбой. Например: у меня есть контроллеры представлений, и когда я хочу удалить контроллер представления, я удаляю все его дочерние объекты, а затем удаляю из стека (стек: я храню в нем контроллеры представлений, для загрузки нового содержимого и загрузки предыдущее содержимое):

    for (UIView *subView in [contentVc subviews])
         [subView removeFromSuperview];

И я получил

- [CALayer сохранить]: сообщение отправлено на освобожденный экземпляр

сообщение

[actual removeFromParentViewController];

- хороший способ удалить его? И выпустит ли он весь контроллер представления и его подпункты? Потому что вместо removeFromSuperview мое приложение не разбивается. Я не понимаю, что было изменено в iOS 7.1.

И как я могу удалить все subviews в viewController без removeFromSuperview и не удалять my viewController (если я просто хочу добавить новые subviews и удалить содержимое в настоящее время)?

UPDATE:

иногда сбой для:

[myactualviewcontroller.view removeFromSuperview];

- [CALayer сохранить]: сообщение отправлено на освобожденный экземпляр

Почему???

и иногда, если я попытаюсь удалить основной подвью из представления контроллера вида, он получил тот же сбой:

[mainView removeFromSuperview] (mainView - это единственный UIView, добавленный в vc.view)

UPDATE2: (хорошо подробно)

Итак, у меня есть контейнер. Я добавляю UIViewController.view в этот контейнер. И я добавляю представление как subview к UIViewController.view. Это представление не является локальным uiview, то есть его объявлено как implementation{ UIView* mainView }. Когда мой UIViewController будет освобожден, в - (void) dealloc { [mainView removeFromSuperview]; [mainView release] [super dealloc];} В mainView removeFromSuperview произошел сбой моего приложения.

Ответ 1

Ваша программа сбой, потому что вы выпускаете что-то более одного раза. Эта часть очевидна.

Первый шаг в поиске этого - включить обнаружение зомби в отладчике. (Project->Schemes->Edit Scheme->Diagnostics->Enable Zombie Objects). Цель здесь состоит в том, чтобы быстрее разорвать вашу программу. Это приведет вас в отладчик, как только вы попытаетесь получить доступ к освобожденному экземпляру. Иногда это укажет вам в правильном направлении, иногда нет, но всегда лучше обнаружить его как можно ближе к проблеме.

Следующий шаг - использовать инструмент Zombies. Этот инструмент даст вам больше информации, чем предыдущий шаг, но он более сложный для использования (именно поэтому я сделал его вторым шагом вместо шага 1). Инструмент "Зомби" будет отслеживать все ваши распределения и релизы и обнаруживать, когда вы пытаетесь получить доступ к объекту зомби.

Последнее средство - начать комментирование кода. Сначала закомментируйте все, что делает ваша программа, с момента создания контроллера представления (тот, который сбой) и когда вы его отпустите. Затем запустите программу и сделайте все, что вам нужно, чтобы отобразить плохой контроллер просмотра. Очевидно, это ничего не сделает, потому что теперь это просто пустой контроллер, но он не должен терпеть крах). Затем запустите раскодирование блоков кода, немного за раз, и продолжайте работать между каждой итерацией. Это повторяющийся процесс и может быть утомительным, если ваш код контроллера просмотра является большим и сложным. Но идея состоит в том, чтобы постепенно добавлять код обратно, пока вы не добавите что-то обратно, и он сработает - тогда вы знаете, что нашли фрагмент кода, который вызывает проблему. Вы должны быть креативными здесь и тщательно выбирать, как вернуть свой код - если ваша программа имеет хороший модульный дизайн, вы можете сделать это без особых проблем. Кодексу спагетти будет сложно сделать это, но это может дать вам хорошую возможность перестроить ваш код, пока вы на нем. Пройдя этот процесс, вы сузите проблему и, в конце концов, найдете ошибку в процессе устранения.

Ответ 2

Как правило, не рекомендуется изменять массив, когда вы быстро перечисляете его. Кажется, вы используете быструю перечисление в массиве представлений subviews и одновременно изменяете этот массив (удаляя subviews по мере того, как вы идете). Вы можете попробовать что-то вроде этого:

NSArray *subviewsCopy = [[contentVc subviews] copy];
for (UIView *subview in subviewsCopy) {
    [subview removeFromSuperview];
}

Однако, как говорили некоторые другие, немного странно, что вам нужно решить проблему удаления этих подзонов вручную. В нормальных условиях просмотр контроллера представления (и иерархия представления под ним) будет автоматически очищаться, когда сам диспетчер представлений будет освобожден.

Есть также некоторые хорошие инструменты, которые могут помочь вам отследить источник проблемы. В частности, вы должны профилировать свое приложение (в Xcode, в меню продукта) и выбрать инструмент "Зомби", когда "Инструменты" подскажут вам. С помощью Zombies вы можете увидеть историю сохранения/освобождения объекта, который был отправлен после того, как он был освобожден.

Если вы пытаетесь выполнить эту ручную очистку иерархии представлений, потому что вы подозреваете, что ваши представления будут утечки в противном случае, я предлагаю вам также попробовать инструмент "Утечки" в "Инструменты" и убедиться, что когда этот код отключен, соответствующие представления на самом деле утечка информации.

Ответ 3

ОБНОВЛЕНО

попробуйте сделать это:

NSArray *subviews = [NSArray arrayWithArray:[contentVc subviews]];
for (UIView *subView in subviews)
     [subView removeFromSuperview];

Я думаю, что вы получили ошибку сбоя, которую вы пытаетесь быстро перечислить массив с переменной длиной (фактически, когда вы удаляете subview, он также удаляется из массива subview).

Если вы хотите удалить диспетчер просмотра, просто вызовите:

[contentVc.view removeFromSuperView];
[contentVc removeFromParentViewController];

Ответ 4

иногда сбой для:

[myactualviewcontroller.view removeFromSuperview];

Вы не должны добавлять или удалять представление контроллеров из иерархии представления вручную, а скорее полагаться на UIWindow rootViewController, нажимать ваш контроллер на UINavigationController и т.д., чтобы система могла добавить представление для частных подчиненных наблюдателей. Если вы не создаете Пользовательский контейнерный контроллер просмотра, который, как я полагаю, вы не знаете.

Если вы просто хотите обрабатывать представления вручную, не используйте контроллеры представлений, так как они не будут сохраняться системой, и они не получат никаких сообщений о ротации и т.д. Поэтому использование диспетчера представлений в этом случае бессмысленно.

Как и для обработки памяти subview, subviews сохраняются с помощью своего супервизора, поэтому, пока вы не сохраняете ссылку strong, вам не нужно освобождать subviews, просто удалите общий супервизор.

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

Наконец, вы должны начать использовать ARC.

Ответ 5

сделайте следующее:

1- отлаживаем for (UIView *subView in [contentVc subviews]) и проверяем, сколько раз оно повторяется.

если он не сбой в первом ударе, вы можете добавить эту строку перед удалением представления   if (subView.superView != nil)

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

UPDATE2: я буду считать, что вы будете знать об утечках памяти, и что у вас есть хороший опыт в этом.

  • всякий раз, когда вы добавляете subview в представление, это сохранит объект на 1 в дополнение к оригиналу 1, который будет равен 2, а затем сразу после добавления подзаголовка вы должны его выпустить, что уменьшит сохраните счет обратно на один. вот трюк: вам не нужно выпускать subview или удалять его из родительского представления, чтобы избавиться от оставшегося количества счета. вы можете просто удалить или освободить родительский вид. в качестве примера возьмем NSMutableArray.

  • удалите [mainView removeFromSuperview]; из метода dealloc:. вы можете добавить его еще там, где метод viewWillDisappear:. Метод dealloc не должен содержать ничего, кроме вызовов выпуска.

Ответ 6

1. В соответствии с документацией Apple вызов метода removeFromSuperview удалит это представление из супервизора и автоматически отпустит его.

Итак, если вы используете removeFromSuperview, тогда вы не должны вызывать [release], что приведет к сбою вашего приложения.

Обратитесь к этому скриншоту из Apple.

enter image description here

В вашей реализации dealloc вы так себя чувствуете

- (void) dealloc {

    // Removed from Parent view and released.
    [mainView removeFromSuperview];

    // no mainView exists in memory , so it crashed the App.
    [mainView release];// Comment this line to avoid the crash

    [super dealloc];
}

2. Вы не должны отключать указанный контейнер.

You are having like this,

for (UIView *subView in [contentVc subviews])
     [subView removeFromSuperview];

Вместо этого вы можете реализовать тот же эффект, имея эту одну строку от Apple.

[[contentVc subviews] makeObjectsPerformSelector:@selector(removeFromSuperview)];

Ответ 7

Пожалуйста, убедитесь, что все возможные делегаты удалены до удаления представлений (т.е. someScrollViewDelegate = nil; до [someScrollView removeFromSuperview];) завершена/или анимация (все CATransaction, [UIViev beginAnimation...], [UIView animateWithDuration...] и т.д.).

Ответ 8

Вместо:

for (UIView *subView in subviews)
     [subView removeFromSuperview];

Try:

[subviews makeObjectsPerformSelector:@selector(@"removeFromSuperview");