#iphone #uiwebview #exc-bad-access
#iPhone #uiwebview #исключение-bad-access
Вопрос:
У меня сбой приложения, которое использует UIWebView. Обычно это происходит, когда страница загружена не полностью, и UIWebView отправляет селектор остановки загрузки. Или когда UIWebView полностью загрузил страницу. У меня есть EXC_BAD_ACCESS
. Стек выглядит следующим образом:
#0 0x95bb7688 in objc_msgSend
#1 0x30a671db in -[UIWebView webView:decidePolicyForNavigationAction:request:frame:decisionListener:]
#2 0x3024a10d in __invoking___
#3 0x30249ff8 in -[NSInvocation invoke]
#4 0x358ab160 in HandleDelegateSource
#5 0x302452c1 in CFRunLoopRunSpecific
#6 0x30244628 in CFRunLoopRunInMode
#7 0x32044c31 in GSEventRunModal
#8 0x32044cf6 in GSEventRun
#9 0x309021ee in UIApplicationMain
#10 0x0000239c in main at main.m:13
для меня самым странным здесь является webView:decidePolicyForNavigationAction:request:frame:decisionListener:
селектор, отправленный в UIWebView, потому что в документации UIWebView такого селектора нет! Только для Cocoa (не для cocoa touch) WebView.
Я подозреваю, что что-то не так с UIWebView или его делегатом. Но я не могу установить точку останова для их просмотра. Пожалуйста, посоветуйте, как я могу получить больше информации в этой ситуации.
Комментарии:
1. Как насчет [UIWebView WebView:decidePolicyForNavigationAction:request:frame:decisionListener:]??
Ответ №1:
Вы должны прекратить загрузку WebView и удалить делегата перед выходом из представления:
// ARC (correct solution)
- (void)dealloc {
[_webView setDelegate:nil];
[_webView stopLoading];
}
// non ARC
- (void)dealloc {
[webView setDelegate:nil];
[webView stopLoading];
[webView release];
[super dealloc];
}
// ARC (older solution)
- (void)viewWillUnload {
[webView setDelegate:nil];
[webView stopLoading];
}
Что говорится в документации Apple:
Важно перед выпуском экземпляра UIWebView, для которого вы установили делегат, вы должны сначала установить его свойству delegate значение nil. Это можно сделать, например, в вашем методе dealloc.
Комментарии:
1. В ios 6 используйте метод dealloc с ARC (без вызова super или какого-либо кода освобождения памяти) — viewWillUnload устарел в ios 6.
2. Это не работает в iOS 6, как указал @MortenHolmgaard. Смотрите мой ответ ниже.
3. Я не понимаю, почему dealloc — это версия, отличная от arc. В ARC вы не можете вызвать dealloc вручную, но он вызывается системой автоматически, и код в dealloc выполняется.
4. Использование dealloc должно быть стандартным методом для arc и не-arc. в версии arc опустите [WebView release]; и [super dealloc];. При использовании приведенного выше решения, если вы представите полноэкранный режим, будет вызван режим -viewWillDisappear:, и вам придется перепрограммировать ваш webview при вызове режима -viewWillAppear:.
5. использование [_webView setDelegate:nil]; спасите мой день! У меня есть viecontrolle, который вызывает другой, использующий webview, и произошел сбой. Теперь это работает!
Ответ №2:
Попробуйте включить NSZombie и посмотрите, не выходит ли что-то слишком рано.
Возможно, вы отменяете загрузку представления, а затем немедленно разрушаете иерархию представлений, в результате чего что-то должно быть выпущено до того, как UIWebView закончит возиться с этим.
В этом случае обратная трассировка выглядит так, как будто это делегат, который выпускается досрочно. Отношения делегирования, как правило, слабые и из-за отсутствия GC являются прекрасным источником оборванных ссылок, которые вызывают сбои, похожие на эти.
Ответ №3:
Просмотр исчезнет — это рабочий вариант принятого ответа:
// ARC
- (void)viewWillDisappear:(BOOL)animated{
[self.webView setDelegate:nil];
[self.webView stopLoading];
}
Это отлично работает для iOS 6.
Ответ №4:
У меня был сбой EXC_BAD_ACCESS при прокрутке UIWebView, но только на iPad и только тогда, когда пользователь оставил прокрутку UIWebView, когда он / она закрыл содержащий его контроллер просмотра.
Установка для делегата значения nil не решила мою проблему здесь, но я нашел решение в другом месте по другой проблеме. Я добавил этот код в метод, вызываемый моей кнопкой закрытия:
for (id subview in webView.subviews){
if ([[subview class] isSubclassOfClass: [UIScrollView class]]){
[subview setContentOffset:CGPointZero animated:NO];
}
}
Это останавливает прокрутку перед вызовом метода dealloc, в чем, по-видимому, и заключается проблема.
Ответ №5:
Я также видел эту точную ошибку, и она была вызвана тем, что делегат, который я назначил для UIWebView, не был сохранен (в моем случае UIViewController).
Ответ №6:
Обработка отмены регистрации делегата из webview и остановка загрузки webview лучше всего обрабатываются методом dealloc ViewController.
В качестве примера, когда viewWillDisappear может завершиться с ошибкой: если ViewController является дочерним по отношению к другому ViewController, вы можете запустить удаление ViewController view из родительского ViewController view с анимацией. В то же время вы можете удалить ViewController из его родительского элемента и обнулить его ссылку. В этот момент ViewController будет равен нулю, и viewWillDisappear никогда не будет вызван, что означает, что делегат WebView никогда не будет очищен.
Используйте dealloc и убедитесь, что ваш WebView всегда очищается.