Які витоки автоматичного підрахунку посилань у Objective-C не запобігають чи мінімізують?


235

На платформах Mac та iOS витоки пам’яті часто викликаються невипущеними покажчиками. Традиційно завжди було надзвичайно важливо перевірити свої виплати, копії та зберігання, щоб переконатися, що кожен має відповідне повідомлення про випуск.

Ланцюжок інструментів, що постачається з Xcode 4.2, вводить автоматичний підрахунок посилань (ARC) з останньою версією компілятора LLVM , що повністю усуває цю проблему, отримуючи компілятор для управління пам’яттю для вас. Це досить круто, і це дозволяє скоротити багато непотрібного, щоденного часу розробки та запобігти багато необережних витоків пам'яті, які легко виправити за допомогою належного балансу збереження / випуску. Навіть пулами автоматичного випуску потрібно керувати по-різному, коли ви вмикаєте ARC для своїх додатків для Mac та iOS (як ви більше не повинні виділяти власні NSAutoreleasePools).

Але які інші витоки пам’яті не заважають мені все-таки слідкувати?

Як бонус, які відмінності між ARC на Mac OS X та iOS та збиранням сміття на Mac OS X?

Відповіді:


262

Основна проблема, пов’язана з пам’яттю, яку ви все ще повинні знати, - це збереження циклів. Це відбувається, коли один об’єкт має сильний вказівник на інший, але цільовий об'єкт має сильний покажчик назад до оригіналу. Навіть коли всі інші посилання на ці об’єкти будуть видалені, вони все одно будуть триматися один за одного і не будуть випущені. Це також може статися опосередковано ланцюжком об'єктів, у якого може бути останній ланцюг, що посилається на попередній об'єкт.

Саме з цієї причини існують __unsafe_unretainedі __weakкваліфікаційні права власності. Перший не збереже жодного об'єкта, на який він вказує, але залишає відкритим можливість того, що цей об’єкт відходить, і він вказує на погану пам'ять, тоді як другий не зберігає об'єкт і автоматично встановлює нуль, коли його ціль розміщена. З двох, __weakяк правило, переважніше на платформах, які його підтримують.

Ви б використовували ці класифікатори для таких речей, як делегати, де ви не хочете, щоб об'єкт зберігав свого делегата і потенційно призводив до циклу.

Ще одна значна проблема, пов'язана з пам'яттю, - це обробка об'єктів Core Foundation та пам'яті, виділених за допомогою malloc()таких типів char*. ARC не керує цими типами, а лише об'єктами Objective-C, тому вам все одно доведеться з ними боротися самостійно. Типи основних фондів можуть бути особливо складними, оскільки іноді їх потрібно переадресувати, щоб вони відповідали об'єктам Objective-C, і навпаки. Це означає, що управління потрібно переносити назад та назад від АРК при з'єднанні між типами CF та Objective-C. Додано кілька ключових слів, пов’язаних із цим з'єднанням, і Майк Еш має чудовий опис різних мостинкових справ у своєму тривалому написанні ARC .

Окрім цього, є ще кілька менш частих, але все ще потенційно проблемних випадків, які опублікована специфікація детально описується.

Значна частина нової поведінки, заснованої на утриманні об'єктів довкола, доки на них є сильний покажчик, дуже схожа на збирання сміття на Mac. Однак технічні основи дуже різні. Цей стиль управління пам’яттю замість того, щоб проводити процес збирання сміття, який проходить через регулярні проміжки часу, щоб очистити об'єкти, на які вже не вказували, цей стиль управління пам’яттю покладається на жорсткі правила збереження / випуску, яких нам потрібно дотримуватися в Objective-C.

ARC просто приймає повторювані завдання управління пам’яттю, які ми мали виконувати роками, і завантажує їх у компілятор, тому нам більше не доведеться турбуватися про них знову. Таким чином, у вас не виникає проблем із зупинкою чи профілями пам'яті, що виникають на платформах, зібраних сміттям. Я пережив це обоє у своїх зібраних сміттям додатках для Mac, і нетерпляче бачу, як вони поводяться під ARC.

Докладніше про збирання сміття проти ARC дивіться у цій дуже цікавій відповіді Кріса Леттнера у списку розсилки Objective-C , де він перераховує багато переваг ARC над збиранням сміття Objective-C 2.0. Я зіткнувся з кількома проблемами GC, які він описує.


2
Дякую за детальну відповідь. У мене була та сама проблема, де я визначив делегата під _unsafe_unretain, і мою програму було розбито, пізніше виправили її, змінивши на сильну, але тепер у неї витоку пам'яті. Отже, я змінив її на слабку і працює як шарм.
чатхурам

@ichathura Нічого собі! Ти врятував мене від багна АРК. Я зіткнувся з таким самим збоєм під час використання CMPopTipView.
Nianliang

@BradLarson: "Ви не маєте проблем із зупинкою або профілями пам'яті пилових зубів на платформах, зібраних зі сміттям". Я очікував би гірших профілів пам’яті зупинки та пилоподібних даних від меліорації, що базується на масштабах, та набагато гіршої продуктивності від підрахунку довідок, тому я хотів би побачити реальне порівняння.
Джон Харроп

Бред, посилання від Кріса Леттнера мертва . Я не на 100%, але знайшов це інше посилання. Я думаю, це те, що ви хотіли пов’язати: списки.swift.org/pipermail/swift-evolution/Week-of-Mon-20160208/…
мед

1
@Honey - Дякую, що вказали на це. Той, на який ви посилаєтеся, дещо відрізняється, але я замінив мертве посилання на архівну версію оригінального повідомлення. Це в архівах списків розсилки, які повинні бути десь доступні, але я дивлюсь, чи зможу знайти їхнє нове місцезнаходження.
Бред Ларсон

14

ARC не допоможе вам з пам'яттю не ObjC, наприклад, якщо вам malloc()щось потрібно, вам все одно потрібно free().

ARC можна обдурити, performSelector:якщо компілятор не може зрозуміти, що таке селектор (компілятор генерує попередження про це).

ARC також генерує код за умовами іменування ObjC, тому якщо ви змішаєте код ARC та MRC, ви можете отримати дивовижні результати, якщо код MRC не виконає те, що компілятор вважає обіцяними іменами.


7

У моїй програмі виникли витоки пам'яті через наступні 4 проблеми:

  1. Недійсні NSTimers при відхиленні контролерів перегляду
  2. Забувши видалити будь-яких спостерігачів до NSNotificationCenter при відхиленні контролера перегляду.
  3. Зберігання чітких посилань на себе в блоках.
  4. Використання чітких посилань на делегатів з огляду на властивості контролера

На щастя, я натрапив на наступне повідомлення в блозі і зміг їх виправити: http://www.reigndesign.com/blog/debugging-retain-cycles-in-objective-c-four-likely-culprits/


0

ARC також не буде керувати типами CoreFoundation. Ви можете їх "мостити" (Використовуючи CFBridgingRelease()), але тільки якщо ви збираєтесь використовувати його як об'єкт Objective-C / Cocoa. Зауважте, що CFBridgingRelease просто зменшує кількість CoreFoundation, що зберігає кількість на 1, і переміщує її до ARC об'єктива-C.


0

Xcode 9 - це чудовий інструмент для пошуку подібних проблем. Він називається: " Графік налагодження пам'яті ". Використовуючи його, ви можете знайти об'єкт, що протікає, за типом класу, і ви можете чітко бачити, хто має чітке посилання на нього, відпустивши його звідти, вирішує вашу проблему. Він також виявляє цикли пам'яті.

Докладніше про те, як ним користуватися

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.