Есть ли способ «найти тайные остатки» …?

#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?

Это отлично подходит для получения нижней части объектов, которые не освобождаются.