Вызов -retainCount считается вредным - PullRequest
38 голосов
/ 26 апреля 2011

Или, почему я не использовал retainCount на моих летних каникулах

Этот пост предназначен для получения подробных описаний причин и причин этого печально известного метода,retainCount, для консолидации соответствующей информации, плавающей вокруг SO. *

  1. Основы: Каковы официальные причины не использовать retainCount?Есть ли когда-нибудь ситуация вообще , когда это может быть полезно?Что нужно сделать вместо этого? ** Не стесняйтесь редактировать.

  2. Историческая справка: почему Apple предоставляет этот метод в протоколе NSObject , если он не предназначен для использования?Код Apple использует для некоторых целей retainCount?Если так, то почему он где-то не спрятан?

  3. Для более глубокого понимания: каковы причины, по которым у объекта может быть другой счет сохранения, чем предполагалось бы из кода пользователя?Можете ли вы привести примеры *** стандартных процедур, которые может использовать базовый код, которые вызывают такую ​​разницу?Известны ли случаи, когда количество сохраняемых данных всегда отличается от ожидаемого новым пользователем?

  4. Что еще, по вашему мнению, стоит упомянуть о retainCount?


* Кодеры, не знакомые с Objective-C и Cocoa, часто сталкиваются или, по крайней мере, неправильно понимают схему подсчета ссылок.В объяснениях учебника может быть упомянуто количество сохраняемых значений, которое (согласно этим объяснениям) увеличивается на единицу при вызове retain, alloc, copy и т. Д. И уменьшается на единицу при вызове release (и в некоторых случаях).точка в будущем, когда вы звоните autorelease).

Начинающий хакер Какао, Крис, может, таким образом, довольно легко прийти к мысли, что проверка счетчика сохранения объекта будет полезна при разрешении некоторыхпроблемы с памятью, и, о чудо, есть метод, доступный для каждого объекта с именем retainCount!Крис звонит retainCount на пару объектов, и этот слишком высок, а этот слишком низок, и что, черт возьми, происходит ?!Итак, Крис делает сообщение на SO: «Что не так с моим управлением памятью?»и затем рой букв , спускается со словами «Не делай этого! Ты не можешь положиться на результаты», что хорошо, но наш отважный кодировщик может захотеть более глубокого объяснения.

Я надеюсь, что это превратится в FAQ, страницу хороших информационных очерков / лекций от любого из наших экспертов, которые склонны написать один, что новые головы какао могут бытьуказал, когда они задаются вопросом о retainCount.

** Я не хочу делать это слишком широким, но конкретные советы из опыта или документы по проверке / устранению неполадок сохраняют и выпускают пары здесь могут быть уместны.

*** в фиктивном коде;очевидно, что широкая публика не имеет доступа к фактическому коду Apple.

Ответы [ 2 ]

29 голосов
/ 26 апреля 2011

Основы: Каковы официальные причины отказа от использования retainCount?

Управление автоматическим выпуском является наиболее очевидным - вы не можете быть уверены, сколько ссылок представленоretainCount находятся в локальном или внешнем (во вторичном потоке или в локальном пуле другого потока) пуле автоматического выпуска.

Кроме того, у некоторых людей возникают проблемы с утечками, а также с подсчетом ссылок более высокого уровня и тем, какАвто-релизы работают на фундаментальных уровнях.Они напишут программу без (большого) отношения к правильному подсчету ссылок или без правильного подсчета ссылок.Это делает их программу очень трудной для отладки, тестирования и улучшения - это также очень трудоемкое исправление.

Причина, препятствующая ее использованию (на уровне клиента), двояка:

1) Значение может варьироваться по многим причинам. Одной лишь многопоточности является достаточной причиной, чтобы никогда ей не доверять.

2) Вам все еще нужно реализовать правильный подсчет ссылок.retainCount никогда не спасет вас от несбалансированного подсчета ссылок.

Есть ли вообще какая-либо ситуация, когда она может быть полезна?

Вы может фактически использовать его осмысленно, если вы написали свои собственные распределители или схему подсчета ссылок, или если ваш объект жил в одном потоке, и у вас был доступ к любым и всем пулам автоматического выпускаон может существовать в. Это также означает, что вы не будете делиться им с какими-либо внешними API.Самый простой способ смоделировать это - создать программу с одним потоком, нулевым пулом автоматического выпуска и выполнить подсчет ссылок «нормальным» способом.Маловероятно, что вам когда-нибудь понадобится решить эту проблему / написать эту программу по каким-либо иным причинам, кроме «академических».

В качестве средства отладки: вы могли бы использовать его, чтобы убедиться, чтосохранить количество не является необычно высоким.Если вы выберете такой подход, помните о различиях в реализации (некоторые процитированы в этом посте) и не полагайтесь на них.Даже не передавайте тесты в свой репозиторий SCM.

Это может быть полезно для диагностики в крайне редких случаях.Его можно использовать для обнаружения:

  • Чрезмерное удержание: распределение с положительным дисбалансом в счетчике удержания не будет отображаться как утечка, если распределение доступно вашей программе.

  • Объект, на который ссылаются многие другие объекты. Одной из иллюстраций этой проблемы является (изменяемый) общий ресурс или коллекция, которая работает в многопоточном контексте - частый доступ или изменения к этому ресурсу / коллекцииможет привести к значительному узкому месту в выполнении вашей программы.

  • Уровни автоматического освобождения: автоматическое освобождение, пулы автоматического освобождения и циклы сохранения / автоматического освобождения - все это обходится дорого.Если вам необходимо минимизировать или уменьшить использование и / или увеличение объема памяти, вы можете использовать этот подход для выявления чрезмерных случаев.

Из комментария с Bavarious (ниже): высокое значение также может указыватьнедействительное распределение (экземпляр dealloc'd).Это полностью деталь реализации, и опять же, ее нельзя использовать в производственном коде.Сообщение об этом распределении может привести к ошибке при включении зомби.

Что нужно сделать вместо этого?

Если вы не несете ответственности за возврат памяти на self (то есть вы не написали распределитель), оставьте его в покое - это бесполезно.

Вы должны научиться правильному подсчету ссылок.

Для лучшего понимания использования выпуска и автоматического выпускаустановите некоторые контрольные точки и поймите, как они используются, в каких случаях и т. д. Вам все равно придется научиться правильно использовать подсчет ссылок, но это может помочь вам понять, почему это бесполезно.

Еще проще: используйте инструменты для отслеживания выделения и подсчета ссылок, затем анализируйте подсчет ссылок и стеки вызовов для нескольких объектов в активной программе.

Историческая справка: почему Apple предоставляет этот метод в протоколе NSObject, если он не предназначен для использования? Код Apple полагается на retainCount для какой-то цели? Если так, то почему он не спрятан где-то?

Мы можем предположить, что он публичный по двум основным причинам:

1) Правильный подсчет ссылок в управляемых средах. Распределители могут использовать retainCount - действительно. Это очень простая концепция. Когда вызывается -[NSObject release], может быть вызван счетчик ссылок (если он не переопределен), и объект может быть освобожден, если retainCount равно 0 (после вызова dealloc). Это все хорошо на уровне распределителя. Распределители и зоны (в основном) абстрагированы, так что ... это делает результат бессмысленным для обычных клиентов. См. Комментарий к bbum (ниже), чтобы узнать, почему retainCount не может быть равно 0 на уровне клиента, освобождение объекта, последовательности освобождения и т. Д.

2) Сделать его доступным для субклассеров, которым нужно пользовательское поведение, а также потому, что другие методы подсчета ссылок являются общедоступными. В некоторых случаях это может быть удобно, но обычно используется по неправильным причинам (например, бессмертные одиночки). Если вам нужна собственная схема подсчета ссылок, возможно, стоит переопределить эту семью.

Для более глубокого понимания: каковы причины того, что объект может иметь другое количество сохранений, чем предполагалось бы из кода пользователя? Можете ли вы привести примеры *** стандартных процедур, которые может использовать базовый код, которые вызывают такую ​​разницу? Известны ли случаи, когда счет сохранения всегда отличается от ожидаемого новым пользователем?

Опять пользовательские схемы подсчета ссылок и бессмертных объектов. NSCFString литералы попадают в последнюю категорию:

NSLog(@"%qu", [@"MyString" retainCount]); 
// Logs: 1152921504606846975

Что еще, по вашему мнению, стоит упомянуть о retainCount?

Это бесполезно в качестве средства отладки. Научитесь использовать анализ утечек и зомби, и используйте их часто - даже после у вас есть ручка подсчета ссылок.


Обновление: bbum недавно опубликовал статью под названием retainCount бесполезен . Статья содержит подробное обсуждение того, почему -retainCount бесполезен в подавляющем большинстве случаев.

1 голос
/ 26 апреля 2011

Общее правило: если вы используете этот метод, вам лучше быть чертовски уверенным, что вы знаете, что делаете.Если вы используете это для отладки утечки памяти, вы делаете это неправильно, если вы делаете это, чтобы увидеть, что происходит с объектом, вы делаете это неправильно.

Есть один случайгде я использовал это, и нашел это полезным.Это делается в кеше общего объекта, где я хотел очистить объект, когда на него больше не было ссылки.В этой ситуации я ждал, пока retainCount не станет равным 1, и затем я могу освободить его, зная, что его больше ничего не держит, это, очевидно, не будет работать должным образом в средах со сборкой мусора, и есть более эффективные способы сделать это.Но это все еще единственный «действительный» вариант использования, который я видел для него, и это не то, что многие люди будут делать.

...