2014-09-04 2 views
4

Представьте себе следующий сценарий, используя управление памятью вручную (иначе не-ARC):Слабый/Сильный «танец» в ручном управлении памятью

У меня есть VC, который проходит через блок к методу класса. Перед выполнением блока VC выдается из UINavigationController. Слабая ссылка в форме __block MyVC *weakSelf = self передается блоку, который затем преобразуется в MyVC *strongSelf = weakSelf (он же слабый/сильный танец). Блок никогда не сохраняется ни одним из участников.

В этом случае, что я вижу в моем коде:

  1. dealloc из ВК вызывается, когда она выскочила.
  2. Блок в конечном итоге называется.
  3. Приложение вылетает из-за того, что я обращаюсь к мусору (strongSelf указывает на него).

Мой вопрос: я не хочу, чтобы мой VC оставался в живых до тех пор, пока блок в конечном счете не исполнится, но как только блок будет выполнен, я хочу подтвердить, что действителен strongSelf.

Я проверил это (non-trivial example by Apple), который не работает, потому что он разработан с учетом ARC. Итак, как я могу иметь такое же поведение с MMM? В идеале я хочу иметь то, что делает __weak: если retainCount достигает нуля, я хочу, чтобы его ссылки указывали на ноль, а не на мусор.


После прочтения этого от Apple:

В некоторых случаях вы можете использовать __unsafe_unretained если класс не __weak совместимы. Это может, однако, стать непрактичным для нетривиальных циклов, потому что может быть трудно или невозможно проверить , что указатель __unsafe_unretained по-прежнему действителен и по-прежнему указывает на тот же объект.

Поскольку у меня нет доступа к __weak, что я хочу сделать, это возможно?

+0

@trojanfoe точка, которую он знает, если безопасно вызывать методы внутри блока. Если VC исчезнет, ​​он потерпит крах, если он все еще существует, он будет работать, как ожидалось. – Peres

+1

Вы можете делать то, что вы пытаетесь достичь, но это не тривиально. Я пишу ответ, поскольку пару лет назад я должен был решить эту проблему. Будьте на связи! –

+0

@trojanfoe, если бы я сделал это, мне пришлось бы проверять всюду, если блок «NULL», прежде чем пытаться его выполнить. – Peres

ответ

1

Я сражался с этой точной проблемой в iOS 4.x дней. Это непросто без слабых указателей, дающих вам руку!

Если вам гарантированно, что блок выполняется в основном потоке в более поздней точке (то есть, где strongSelf назначается из weakSelf), вам в основном нужно место для хранения флага «isDead», который вы устанавливаете, когда VC является dealloced. Затем вы проверяете этот флаг перед тем, как делать что-либо со слабой командой/strongSelf. Одним из решений является следующее:

  1. Вам нужен класс, который является единственным заданием - хранить этот флаг isDead в переменной-члене. Что-то вроде NSMutableBoolean. Я не буду писать один, это достаточно просто, ему просто нужно одно свойство BOOL.
  2. В вашем методе VC -[NSObject init] вы создаете экземпляр этого объекта флага, который изначально должен быть установлен на false.
  3. Затем вы передаете этот объект на любой блок, который вы ставите в очередь для последующего исполнения, чтобы он автоматически сохранялся/отпускался блоком (т. Е. Без прохождения слабого/сильного танца).Внутри блока вы проверяете, установлен ли флаг NO, прежде чем делать что-либо со слабой командой.
  4. Ключом является, конечно же, установить флаг YES внутри метода -[NSObject dealloc] VC, а затем отпустить его. Если какие-либо блоки все еще ждут выполнения, они уже сохранят объект флага, и когда они будут выполнены позже, они запросят флаг, чтобы обнаружить, что VC теперь мертв.

Это звучит громоздко (и это, немного), но идея состоит в том, что флаг «isDead» живет вне сферы действия ОК и, следовательно, не привязан к его срок службы. Что касается безопасности потоков, пока вы устанавливаете/запрашиваете флаг внутри метода init/dealloc VC и когда блок выполняется (в основном потоке, а не на фоновом потоке), он будет потокобезопасным.

0

Я не хочу, чтобы мой VC, чтобы остаться в живых, пока блок в конечном счете не выполняет

Но почему это важно? Память одного VC не должна быть такой, и если блок выполняет любые действия пользовательского интерфейса в VC, это тоже нормально, поскольку VC пока не отображается.

В принципе, блок не должен фиксировать слабую ссылку на VC, если VC не сохраняет блок. Использование ненулевой слабой ссылки означает, что вы гарантируете, что объект останется в живых, что здесь не так. Я бы посоветовал вам не делать этого слабого.

+0

Вы принимаете предположение: 'block выполняет любые действия пользовательского интерфейса в VC, это тоже нормально, поскольку VC не отображается в любом случае', что, если он имеет некоторые серьезные побочные эффекты? Образцы изображений? То, что я пытаюсь избежать, - это дополнительная работа там, где она не нужна. – Peres

Смежные вопросы