Почему объект не освобождается при использовании ARC + NSZombieEnabled - PullRequest
34 голосов
/ 07 декабря 2011

Я преобразовал свое приложение в ARC и заметил, что объект, размещенный в одном из моих контроллеров представления, не освобождается, когда тот контроллер представления был освобожден. Потребовалось время, чтобы понять, почему. Я включил Включить объекты-зомби для своего проекта во время отладки, и это оказалось причиной. Рассмотрим следующую логику приложения:

1) Пользователь вызывает действие в RootViewController, которое вызывает создание и представление SecondaryViewController с помощью presentModalViewController:animated.

2) SecondaryViewController содержит ActionsController, который является NSObject подклассом.

3) ActionsController наблюдает уведомление через NSNotificationCenter, когда оно инициализируется, и прекращает наблюдение, когда оно освобождается.

4) Пользователь отклоняет SecondaryViewController, чтобы вернуться к RootViewController.

При отключенной опции «Включить объекты-зомби» все вышеперечисленное работает нормально, все объекты освобождаются. Если параметр «Включить объекты-зомби» включен, ActionsController не освобождается, даже если SecondaryViewController освобожден.

Это вызвало проблемы в моем приложении. B / c NSNotificationCenter продолжает отправлять уведомления на ActionsController, а полученные обработчики вызывают сбой приложения.

Я создал простое приложение, иллюстрирующее это на https://github.com/xjones/XJARCTestApp. Посмотрите журнал консоли с включенным / выключенным включением объектов-зомби, чтобы убедиться в этом.

ВОПРОС (S)

  1. Это правильное поведение Enable Zombie Objects?
  2. Как мне реализовать этот тип логики для устранения проблемы. Я хотел бы продолжить использование Enable Zombie Objects.

РЕДАКТИРОВАТЬ # 1: в соответствии с предложением Кевина я отправил это в Apple и openradar на http://openradar.appspot.com/10537635.

РЕДАКТИРОВАТЬ # 2: уточнение хорошего ответа

Во-первых, я опытный разработчик iOS и полностью понимаю ARC, объекты зомби и т. Д. Если я что-то упускаю, конечно, я ценю любое освещение.

Во-вторых, верно, что обходной путь для этого конкретного сбоя - удалить actionsController в качестве наблюдателя, когда secondaryViewController освобожден. Я также обнаружил, что если я явно установлю actionsController = nil, когда secondaryViewController освобожден, он будет освобожден. Оба они не являются хорошим обходным путем, поскольку они фактически требуют, чтобы вы использовали ARC, а кодировали, как если бы вы не использовали ARC (например, nil iVars явно в dealloc). Конкретное решение также не помогает определить, когда это может быть проблемой в других контроллерах, поэтому разработчики точно знают, когда и как обойти эту проблему.

Хороший ответ объяснил бы, как детерминистически знать, что вам нужно сделать что-то особенное с объектом при использовании ARC + NSZombieEnabled, чтобы это решило этот конкретный пример, а также применимо в целом к ​​проекту в целом без возможности для других подобных проблем.

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

спасибо всем!

Ответы [ 5 ]

9 голосов
/ 27 декабря 2011

Оказывается, я написал какую-то серьезную чепуху

Если бы зомби работали так, как я писал изначально, включение зомби напрямую привело бы к бесчисленным ложным срабатываниям ...

Есть некоторые проблемы-свисание происходит, вероятно, в _objc_rootRelease, поэтому любое переопределение dealloc должно по-прежнему вызываться с включенными зомби.Единственное, что не случится с зомби, это фактический вызов object_dispose - по крайней мере, по умолчанию.

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

Я действительно предполагал, что вообще этого не вижу: поскольку ARC генерирует эти классные .cxx_destruct методы, чтобы избавиться от любого __strongivars класса, я ожидал увидеть этот вызов метода dealloc - если он реализован.

Очевидно, установка NSZombieEnabled в YES приводит к тому, что .cxx_destruct не будетвызвал вообще - по крайней мере, это то, что произошло, когда я отредактировал ваш пример проекта:
отключение зомби приводит к возврату и обоим деаллокам, в то время как изменение по зомби не дает возврата и только одно деаллока.

Если вы 'Интересно, что дополнительное ведение журнала содержится в ветке примера проекта - работает только при запуске: есть две общие схемы для включения / выключения зомби.


Оригинальный (бессмысленный) ответ:

Это не ошибка, а особенность.

И она не имеет ничего общего с ARC.

NSZombieEnabled в основном изменит dealloc для реализации, которая, в свою очередь, isa-изменит этот тип объекта на _NSZombie - фиктивный класс, который взрывается, как только вы отправляете ему любое сообщение.Это ожидаемое поведение и - если я не совсем ошибаюсь - документированное.

7 голосов
/ 18 августа 2012

Это ошибка, которая была подтверждена Apple в Технические вопросы и ответы QA1758 .

Вы можете обойти эту проблему на iOS 5 и OS X 10.7, скомпилировав этот код в свое приложение:

#import <objc/runtime.h>

@implementation NSObject (ARCZombie)

+ (void) load
{
    const char *NSZombieEnabled = getenv("NSZombieEnabled");
    if (NSZombieEnabled && tolower(NSZombieEnabled[0]) == 'y')
    {
        Method dealloc = class_getInstanceMethod(self, @selector(dealloc));
        Method arczombie_dealloc = class_getInstanceMethod(self, @selector(arczombie_dealloc));
        method_exchangeImplementations(dealloc, arczombie_dealloc);
    }
}

- (void) arczombie_dealloc
{
    Class aliveClass = object_getClass(self);
    [self arczombie_dealloc];
    Class zombieClass = object_getClass(self);

    object_setClass(self, aliveClass);
    objc_destructInstance(self);
    object_setClass(self, zombieClass);
}

@end

Более подробную информацию об этом обходном пути вы найдете в моем блоге Отладка с включенными ARC и зомби .

4 голосов
/ 13 июля 2012

Оказывается, это ошибка iOS. Apple связалась со мной и сообщила, что исправили это в iOS 6.

0 голосов
/ 18 мая 2012

Поскольку вы открыли NSZombieEnabled, это позволило объекту не вызывать dealloc и поместить объект в специальное место.Вы можете закрыть NSZombieEnabled и попробовать еще раз.И дважды проверьте, чтобы ваш код имел условие сохранения круга.

0 голосов
/ 07 декабря 2011

, чтобы ответить на второй вопрос, вам нужно удалить наблюдателя из NSNotification - это не даст ему вызвать представление.

Обычно, вы бы делали это в dealloc, но с этой проблемой зомби, возможно, это не вызвали. Может быть, вы могли бы поместить эту логику в viewDidUnload?

...