#iphone #memory-management #xcode4 #retain
#iPhone #управление памятью #xcode4 #сохранить
Вопрос:
Недавно я исправлял чей-то код. Был большой класс, который не освобождал. Вам пришлось бы выпустить 5 или 6 версий, чтобы освободить его.
Я внимательно просмотрел большой класс и в конце концов нашел различные вещи, которые нужно было выпустить.
Это заставило меня задуматься: просто должен быть какой-то действительно простой способ «найти» все остатки на объекте .. я прав?
Итак, есть ли простой способ «найти все остатки» на объекте? Есть ли кнопка в XCode или инструментах, о которой все остальные знают?
Что вы делаете, когда не можете найти тайну сохранения подобным образом?
Итак, во вселенной iOS, если кто-нибудь знает кнопку «Показать, откуда взялись все сохранения на этом объекте» — спасибо!
P.S. Обратите внимание, что утечки нет, и этот вопрос совершенно не связан с утечками.Объект просто «совершенно корректно» не был выпущен.
Позже ..
Поистине поразительное решение от Fabio:
Фабио предложил потрясающее решение этой проблемы. В девяти словах, вот оно:
-(id)retain
{
NSLog(@"%@", [NSThread callStackSymbols]);
return ([super retain]);
}
Это удивительно полезно во многих ситуациях и приводит ко многим другим полезным вещам. Вероятно, ты навсегда сэкономил мне две человеко-недели работы в год, Фабио. Спасибо!
Кстати, если вы только начинаете разбираться с этим и боретесь с выводом, я увидел, что обычно будет много фрагментов с надписью «UINib instantiateWithOwner:». Похоже, что они будут первыми, за ними последуют значительные фрагменты.
Комментарии:
1. Конечно,
CADisplayLink
сохраняется объект. В документации так и говорится : «Вновь созданная ссылка отображения сохраняет цель». Чтобы удалить это сохранение, вы должныinvalidate
CADisplayLink
.2. Интересный, нетривиальный вопрос ( 1). Лучше всего первое предложение 😉
3. Это не работает в ARC. Вам не разрешено вызывать [super retain].
4. «В среде ARC вам нужно будет сначала добавить -fno-objc-arc к флагам компилятора, чтобы вы могли переопределить сохранение и вызвать super»
Ответ №1:
Инструменты могут показать вам стек вызовов для каждого malloc, release и retain для любого объекта Obj-C в вашем приложении без необходимости внесения изменений в код. Это работает, когда вы используете ARC, чего нет в случае с решением от fabio.
Это действительно полезно для поиска этих тайных сохранений — например, когда объект просто не освобождается, когда должен.
Вот как:
- CMD I (Продукт / профиль)
- При появлении инструментов выберите «Распределения» (НЕ утечки)
- Ваше приложение должно запуститься.
- Делайте все, что вызывает появление ваших тайных сохранений.
- Выберите инструмент «Распределение» на левой панели.
- Нажмите CMD 1 или выберите круг с волной в нем справа. На панели в правом нижнем углу отметьте опцию «Количество ссылок на записи». Это важно, иначе будут записаны только malloc и освобождения.
- В окне поиска в правом верхнем углу списка введите имя вашего класса (например, BCMyObject).
- Это фильтрует список «Статистики», чтобы показать, сколько экземпляров вашего класса в данный момент запущено. Столбец #Persistent показывает, сколько экземпляров находится в режиме реального времени.
- Щелкните строку, а затем маленькую стрелку -> рядом с именем класса. Вы увидите, что в панировочных сухарях отображается «Статистика> Сводка распределения> BCMyobject»
- Здесь показаны все экземпляры указанного класса (и какие из них являются активными).
- Выберите экземпляр и снова щелкните стрелку (на этот раз по адресу)
- Теперь вы увидите «Статистика> Сводка распределения> BCMyObject > История: 0xADDRESS» в «хлебных крошках».
- Это будет отображаться каждый раз, когда объект сохраняется или освобождается malloc’d .
- Теперь на левой панели, где была опция «Количество ссылок на запись», нажмите значок, который выглядит как панель с подключенными к ней полями, или нажмите CMD 3.
- Выберите одну из строк, и вы увидите полный стек вызовов, который привел к вызову.
Просто! (иш)
Комментарии:
1. Да, это правильный ответ здесь. Переопределение «сохранить» также было решением (хотя, возможно, немного «хакерским») в некоторые дни, но это НЕ работает для ARC. И этот инструмент instruments — делает.
2. Спасибо за этот ответ — я знал, что вы можете получить эту информацию автоматически с помощью инструмента утечки, но я не мог понять, как получить ее с помощью инструмента распределения, поскольку по умолчанию он показывает только mallocs .
3. Вы, сэр, спасли мне жизнь, и теперь я бесконечно благодарен вам за ваш вклад в сохранение моего рассудка.
4. @BenClayton Мне интересно, почему мы не можем обнаружить их с помощью инструмента утечек вместо проверки распределений? Неисправны инструменты? Редактировать: я пытаюсь отфильтровать экземпляры, которые не будут выпущены к концу приложения. Проверка распределений — один из вариантов, но они не отображаются как утечки в инструменте утечки.
5. @mostruash такой вещи, как утечка к концу работы приложения, не существует. Если приложение закрывается, все, что было выделено им, освобождается. Не имеет значения, была ли она выпущена раньше или нет.
Ответ №2:
Просто догадываюсь… но вы можете перезаписать метод retain пользовательского класса, вызывающего super, и создать хороший NSLog для печати стека вызовов.
Обновите фактическим кодом от Joe
-(id) retain {
NSLog(@"%@", [NSThread callStackSymbols]);
return ([super retain]);
}
Еще одна важная деталь заключается в том, что [NSThread callStackSymbols] возвращает NSArray NSStrings, который можно отфильтровать и использовать для других целей. Например, в сложном и динамическом коде, чтобы проверить, правильно ли метод вызывает запуск другого.
ПРИМЕЧАНИЕ: В среде ARC вам нужно сначала добавить флаги компилятора -fno-objc-arc
to, чтобы вы могли переопределить сохранение и вызвать super.
Комментарии:
1. Я думаю, проблема в том, что вы хотите, чтобы вызывающий объект этого сохранял, поэтому в основном в стеке вызовов появляется один фрейм.
2. @Joe Это должно сработать NSLog(@»%@», [NSThread callStackSymbols]). @Gorilla Мне нравится ваш ответ: лучше, потому что вы видите, что стек хорошо уплотнен Xcode. Отличное решение!
3. @Joe callStackSymbols — это изящный трюк, который я обнаружил в Stack Overflow (я думаю). К сожалению, я не смог найти оригинальный ответ. Рад, что вам понравилось решение!!!
4. Это не работает в ARC. Вам не разрешено вызывать [super retain].
5. потрясающе, Грег, спасибо — интересно, какой «эквивалент» у ARC? возможно, просто верните super.
Ответ №3:
Поместите точку останова в пользовательский класс ‘ сохранить
Вы могли бы установить символическую точку останова для сохранения, а затем установить ее в метод retain пользовательского класса. Проблема здесь в том, что retain — это метод на NSObject
, поэтому вы получите выбор из всех классов objective-c при установке точки останова.
В этом случае было бы лучше перезаписать метод retain пользовательского класса вызовом super, чтобы он ничего не делал, но вы могли бы затем поместить в него точку останова.
Используйте действие точки останова для регистрации вызывающего абонента
Чтобы добавить действие точки останова, дважды щелкните по синему маркеру. Найдите точку останова в списке и нажмите кнопку справа. Затем выберите Debugger command
и добавьте в это поле команду GDB frame 1
, которая покажет вам вызывающего сохранителя. С помощью этого вы автоматически регистрируете все сохранения и откуда они берутся. При регистрации выпусков аналогичным образом вы могли бы проверить, какой был дополнительный выпуск.
Это все еще немного утомительно, но это лучшее, что я могу придумать.
Комментарии:
1. Хм. Я не знаю, является ли это распространенной проблемой. Иногда можно применить какое-нибудь волшебство среды выполнения objective-c, чтобы выяснить что-то, но я не вижу, как вы можете отследить вызов функции.
2. Я делал это в прошлом, когда мне было любопытно, что сохраняло мой объект. Я создал пользовательский подкласс, переопределил
-retain
и просто вызвал его[super retain]
. Если вы устанавливаете точку останова при вызове super, легко просматривать трассировку стека каждый раз, когда это происходит, чтобы увидеть, кто несет ответственность. Для более частых событий сохранения / выпуска вы могли бы даже использовать точку останова звука, чтобы прослушивать эти события во время использования приложения.
Ответ №4:
Инструменты и связанные с ними средства управления памятью — ваш друг. Утечки и зомби — два самых ценных доступных инструмента. Используйте их.
Продукт -> Профиль (или Cmd-I)
Комментарии:
1. @Joe существует множество ресурсов для изучения того, как пользоваться инструментами. Начните с поиска видеороликов WWDC. Что касается переопределения
-retain
, это, вероятно, сработает, но кажется неправильным.2. Итак, если вы допустите утечку этого объекта, Инструменты покажут все остатки объекта.
3. Вопрос абсолютно не связан с утечками или инструментом утечек. Приветствия.
Ответ №5:
К сожалению, программно определить, кому «принадлежит» объект, непросто, поскольку идея «владения объектом» является соглашением о кодировании (если вы не включаете сборку мусора).
Ведение журнала стека часто полезно (обычно я использую несколько точек останова с bt;continue
), но это говорит вам только о функции, которая вызвала сохранение, а не о «общей картине» (например, вы могли бы «передать право собственности» с [ivar2 release]; ivar2 = ivar1; ivar1 = nil;
). Иногда это утечка UIKit, поэтому у вас нет исходного кода, и вам действительно нужно копаться.
Однако, если это не утечка, позвоните -release
несколько раз и посмотрите, где происходит сбой!
Ответ №6:
Вы пробовали использовать «Build amp; Analyse» в Xcode?
Это отлично подходит для получения нижней части объектов, которые не освобождаются.