Утечки памяти с помощью UIWebView и Javascript

Я пытаюсь исправить кучу утечек, которые вызывает мой UIWebView и не может найти их происхождение или обходной путь. Я получаю некоторый контент из Интернета через сетевой запрос, а затем собираю свой HTML и загружаю его на лету:

NSString* body = <some HTML>;
NSString* html = [NSString stringWithFormat:kHTMLTemplate, [self scripts], [self styles], body];
[_webView loadHTMLString:html
               baseURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]]];

Каждый раз, когда появляется новый контент, я снова выполняю loadHTMLString, чтобы обновить веб-представление. Я использую один и тот же веб-вид, тот же самый контроллер, то же самое.

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

Кажется, что существует несколько потоков в S.O. около UIWebView утечки памяти. Я попробовал все предложения, которые я нашел (например, установив NSURLCache в ноль или сбросив его, я попытался освободить существующий UIWebView и выделить новый каждый раз, когда у меня есть новые данные и т.д.), Но ничего не помогло.

Мои исследования до сих пор приводят к одному явному результату: кажется, что утечки присутствуют только в том случае, если HTML, который я загружаю в представление, содержит некоторый Javascript. Если вы заметили строку html выше, она состоит из нескольких компонентов; один [self scripts], который является функцией, которая просто возвращает:

return @"<script type='text/javascript' src='jquery-1.4.4.min.js'></script>"
        "<script type='text/javascript' src='jmy.js'></script>";

Если я удалю это, утечек не будет. Но утечки появляются, как только я добавляю тег <script> в свой HTML. Они даже появляются, если я просто включаю файл jquery (или любой другой файл js):

return @"<script type='text/javascript' src='jquery-1.4.4.min.js'></script>";

Итак, вопрос: есть ли у кого-нибудь представление о том, что здесь происходит? Очевидно, что в Javascript файл в мой HTML создается утечка памяти UIWebView.

Тот факт, что утечки появляются как при повторном использовании одного и того же объекта UIWebView, так и при создании нового экземпляра каждый раз, когда у меня есть контент, приводит меня к мысли, что должно быть что-то в том, как обрабатываются файлы javascript loadHTMLString, что приводит к утечкам.

Кто-нибудь знает, как это можно исправить?

enter image description here

Ответ 1

Наконец-то я нашел ключ к тому, что происходит, и, прежде всего, обходное решение, которое я хотел бы поделиться.

Я могу подтвердить, что простое включение некоторого файла javascript вызывало утечку памяти при перезагрузке веб-представления. Я даже попытался создать файл с содержимым HTML, затем загрузив его в UIWebView через loadRequest и перезагрузив его через reload; утечки всегда были там. Я отправлю для этого радар.

Что меня спасло, я использовал innerHTML для обновления содержимого веб-представления. Вместо того, чтобы полагаться на reload или loadHTMLString, я инициализировал свой веб-представление пустым телом (я имею в виду, что там был раздел head, включая все необходимые файлы JS/CSS), а затем обновил его, установив document.body.innerHTML:

body = [body stringByReplacingOccurrencesOfString:@"\"" withString:@"\\\""];
[webView stringByEvaluatingJavaScriptFromString:[NSString stringWithFormat:@"setBody(\"%@\");", body]];

с setBody определяется следующим образом:

var setBody = function(body) {
    document.body.innerHTML = body;
}

Я получил два преимущества: обновление веб-просмотра стало очень быстрым (это эффект не обновления DOM, который, с другой стороны, не совсем желателен в целом), и не было утечек памяти, которые запускали приложение под Инструменты. Недостатком было то, что мне пришлось точно настроить несколько условий, в которых приложение работало нормально; а именно:

  • загрузка веб-представления (даже с пустой страницы тела) занимает много, поэтому вам нужно синхронизировать первое обновление своего содержимого с тем, когда DOM готов,

  • webViewDidFinishLoading кажется ненадежным: он выполняется до document.readyState становится complete;

  • document.documentElement.height, официальный способ получения высоты страницы тоже не является надежным: обходной способ получения "вычисленного стиля" части body и чтения его значения height.

Надеюсь, что это поможет кому-то еще, кто обнаруживает, что его веб-представления утечки памяти.