Возможно ли иметь утечку памяти под ARC, передавая блок в качестве аргумента, а затем копируя его?Если так, как это предотвратить? - PullRequest
1 голос
/ 09 марта 2012

Я работаю над проектом Objective-C, и у нас включен ARC.В настоящее время я имею дело с двумя отдельными классами, давайте назовем их A и B.

A имеет строгую ссылку на B, а B имеет свойство блока с атрибутом copy.

@interface A {
    B *b;
    ...
}
@end

@interface B {
    ...
}
@property (copy) void (^myBlock)();
@end

Теперь, если внутри метода принадлежит A Я пытаюсь присвоить myBlock:

b.myBlock = ^{ [self doSomething] };

Компилятор (правильно) жалуется:

Сильный захват self в этом блоке можетпривести к циклу сохранения.

Я понимаю это поведение и как его обойти, основываясь на ответах на на этот вопрос .(По сути, цикл формируется, потому что после того, как блок скопирован, он будет содержать сильную ссылку на A, которая имеет сильную ссылку на B, которая, в свою очередь, имеет сильную ссылку на сам блок.)

Теперь, если вместо непосредственного присвоения блока свойству я передаю его методу B:

[b takeMyBlock:^{ [self doSomething] }];

И внутри этого метода я делаю присваивание, компилятор больше не будет жаловаться.

Мой вопрос: это создаст утечку памяти?Если так, как этого избежать?Можно ли как-то аннотировать аргумент метода, чтобы компилятор знал, что аргумент будет скопирован?

Заранее спасибо.

Ответы [ 2 ]

1 голос
/ 10 февраля 2013

Я только что столкнулся с той же самой проблемой. Хотя проблема сама по себе не проявлялась как утечка, на самом деле я обнаружил эту проблему при анализе роста кучи с помощью инструмента Allocations в Instruments, поэтому очевидно, что действительно вызывает проблему с памятью.

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

  1. Документация: Как вы предложили в своем вопросе, четко документируйте что этот метод скопирует переданный ему блок. Это может включает в себя именование параметра блока что-то вроде blockToCopy (Xcode помогает отображать имя параметра блока при использовании автодополнение). Также прокомментируйте объявление метода. Если вы используя инструмент документации, такой как appledoc, это особенно приятно, поскольку ваша документация также появится в диалоге быстрой помощи Xcode когда опция нажимает ваш код.

  2. Никогда не захватывайте self сильно в блоке: Возможно, это лучше практика, так как я не думаю, что это хорошая идея, чтобы сохранить сильный ссылка на self внутри блока. Если у вас есть ссылка self, используйте слабую ссылку. Это, безусловно, позволит избежать ситуации, которую вы описать. (N.B. ссылка на переменную экземпляра также будет держать сильный ссылка на self).

0 голосов
/ 27 июня 2013

В ARC это происходит, если локальная переменная или переменная WEAK теряют ссылку. Память будет немедленно освобождена, поскольку ее счетчик ссылок равен нулю. Однако, если вы перераспределяете переменную STRONG, это определенно вызовет утечку памяти для сильных переменных, даже если счетчик ссылок уменьшится до нуля. В этом случае вы должны установить эту переменную равной NIL перед перераспределением или установить переменную в слабое состояние, если сильное свойство не требуется.

...