Ответ 1
Оказывается, это ошибка iOS. Apple связалась со мной и указала, что они исправили это в iOS 6.
Я преобразовал свое приложение в 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. Чтобы проверить это, выполните следующие действия.
Вопрос (ы)
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.
спасибо всем!
Оказывается, это ошибка iOS. Apple связалась со мной и указала, что они исправили это в iOS 6.
Если бы зомби работали так, как я изначально писал, включение зомби напрямую привело бы к бесчисленным ложным срабатываниям...
Происходит какое-то движение 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
- думмический класс, который взрывается, как только вы отправляете ему какое-либо сообщение. Это ожидаемое поведение и - если я не ошибаюсь - документировано.
Это ошибка, которая была подтверждена 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.
чтобы ответить на второй вопрос, который вам нужен, чтобы удалить наблюдателя из NSNotification - это не позволит ему вызвать представление.
Обычно вы делаете это в dealloc, но с этим вопросом зомби, возможно, он не будет вызван. Может быть, вы могли бы поместить эту логику в viewDidUnload?
Поскольку у вас есть открытый NSZombieEnabled, это позволяет объекту не вызывать dealloc и помещать объект в особое место. вы можете закрыть NSZombieEnabled и повторить попытку. И дважды проверьте, имеет ли ваш код условие сохранения круга.