Подтвердить что ты не робот

Почему объект не отменяется при использовании ARC + NSZombieEnabled

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

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

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

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

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

С отключением объектов Zombie отключены, это работает отлично, все объекты освобождены. При включении объектов Zombie на ActionsController не освобождается, даже если SecondaryViewController освобождается.

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

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

Вопрос (ы)

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

EDIT # 1: по предложению Кевина Я отправил это в Apple и openradar на http://openradar.appspot.com/10537635.

РЕДАКТИРОВАТЬ # 2: пояснение на хороший ответ

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

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

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

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

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

4b9b3361

Ответ 1

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

Ответ 2

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

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

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

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

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

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

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


Исходный (бессмысленный) ответ:

Это не ошибка, а функция.

И это не имеет никакого отношения к ARC.

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

Ответ 3

Это ошибка, которая была подтверждена Apple в Техническом Q & A 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 и Zombies.

Ответ 4

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

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

Ответ 5

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