Играюсь с Objective-C, изучаю его и заодно пишу пару небольших примеров. Один из них связан с интернет программкой, другой – графический движок для себя (вспоминаю OpenGL). В одном месте у мне понадобилось часто использовать однообразные объекты, причем очень много раз. Логично стало не загружать их каждый раз и не освобождать память, а написать кэш, который будет сохранять данные для меня.
В качестве кэша я сделал статичную переменную для менеджера кэша, а сами данные решил размещать в NSMutableDictionary. Это что-то типа хэша. Переменная для хэша была объявлена у класса, а метод получения данных из него выглядит следующим образом:
cachedData = [cache objectForKey:filename]; if (cachedData) { return cachedData; } cachedData = [[MFLData alloc] initWithFilename:filename]; [cache setObject:cachedData forKey:filename]; return cachedData;
Никогда не видевший синтаксиса Objective-C да ужаснись. На самом деле я уже привык к нему. Вполне нормально работает. На C# это будет выглядеть как:
cachedData = cache[filename]; if (cachedData != null) { return cachedData; } cachedData = new MFLData(filename); cache[filename] = cachedData; return cachedData;
Запускаю программу и вижу, что все работает нормально на моем компьютере. Я этот код написал примерно месяц назад и забыл уже. Как только получил свой iPhone решил попробовать на нем. Установил программку на телефон, минут 10 работы в ней и программа перезагружает телефон. Что, думаю, за фигня. Попробовал еще раз. То же самое. Я уже и забыл про свой кэш и сначала думал, что где-то косяк доступа к памяти или еще что.
Вчера запустил профайлер и начал смотреть утечки памяти. Ничего не утекает, потому что я использую autorelease, а с ним не может ничего утекать. Зато заметно, что в те моменты, когда должна происходить загрузка из файлов, происходит скачок в использовании памяти. За 5 минут программа сжирает до 100 мегабайт памяти, что для настольного компьютера фигня из под коня, а для телефона серьезная проблема.
Нечал тестировать свой кэш и вот блин, переменная cache оказывается нигде не проинициализирована. Она просто нулевая. Когда я проверяю, есть ли что в кеше ([cache objectForKey:filename] – в С# аналогом будет cache[filename]), переменная cache не существует и Objective-C просто игнорирует любые сообщения для нулевого объекта, поэтому ничего не падает, все работает, зато в кэше ничего не сохраняется и я просто уничтожаю память телефона в мгновения ока. Пример на C# уже давно шарахнулся бы с исключением.
Этот пример показал мне, почему программы для iPhone и для Apple вообще падают реже. Если где-то вы обратитесь к несуществующей переменной, ошибка будет проигнорирована и вполне вероятен случай, когда все будет нормально работать. Как доказывает FireFox – утечка памяти не является причиной провала программы и можно распространять программы с проливами памяти.
Удивило то, что iPhone перезагружался, когда я съедал память. Может из-за того, что он в режиме разработчика, но я бы ожидал от него вылета программы. ОС вполне в состоянии увидеть, что памяти не достаточно и вырубить мою программу. Тут конечно же и я виноват, потому что не во всех объектах проверяю после alloc была ли выделена память. Я не проверяю как раз в объекте, который создается и уничтожается с данными из кэша. Нужно будет это исправить, но ОС могла бы и помочь, хотя и не обязана. Я тут был виноват, но пользователям телефона будет пофиг, что это программист программы налажал, а не телефон глючит и перезагружается.
Понравилось? Кликни Лайк, чтобы я знал, какой контент более интересен читателям. Заметку пока еще никто не лайкал и ты можешь быть первым
А какую Вы SDK исапользуете7
Стоит последняя XCode и последний SDK 5.1, но компилирую для платформы 4.3.
Михаил, я бы вам посоветовал обратиться в сервис с телефоном, я пишу под иос и сжирание памяти даже более катастрофическими методами приводит лишь к вылету приложения а не к перезагрузке телефона (мои тесты проходит на двух 3GS ,одном 4 и одном 4S).
Программа вылетала на двух разных телефонах, так что не думаю, что это телефон. Скорей всего все же iOS. Сейчас уже не буду издеваться над примером, уже все оптимизировал и сейчас программа жрет всего 20 мегабайт оперативки.
Хотелось бы поближе взглянуть на цельный вариант программы что привела к перезагрузке телефона ) мне такие фокусы ещё не удавались)
О, это еще мелочи. Я как-то года три назад случайно писал в поле класса из селектора класса (статического метода в более привычной терминологии). И компилятор нормально это жрал, отделываясь ворнингом. А ворнингов было много, потому что на тот момент не успели исправить все deprecation ворнинги, коих было немерено после перехода на SDK 3.0. Приложение регулярно падало в селекторах класса, находящегося в следующей единице компиляции, потому что волшебным образом переписывалась isa следующего по компиляции класса. И вот вопрос, почему компилятор счел это semantic warning'ом, а не ошибкой, до сих пор открыт(
Так же открыт вопрос, как можно было без предупреждения разрушить совместимость iOS 5+ с приложениями собранными с более старыми версиями SDK. Причем приложение молча стартует, а затем в некий момент падает имея в колстеке вызовы из фреймворков iOS. Банальная пересборка с новым SDK проблему, внезапно, решает.
Хотите найти еще что-то интересное почитать? Можно попробовать отфильтровать заметки на блоге по категориям.