NSFetchedResultsController хранит ссылку на освобожденный контроллер делегата в раскадровке, вызывая сбой - PullRequest
4 голосов
/ 09 февраля 2012

У меня есть простая таблица UIViewTable с детализацией детализации, реализованной с помощью push-перехода UINavigationController в раскадровке.Время от времени случается так, что контроллер табличного представления, кажется, освобождается, в то время как я нахожусь в подробном представлении, поэтому я получаю известное:

[MyViewController controllerWillChangeContent:]: message sent to deallocated instance

Я объясняю лучше, у меня есть очередь NSOperation, котораязагрузить мои данные асинхронно и заполнить таблицу, как только она только что закончилась.Данные правильно получены и таблица заполнена.Для подробного просмотра я нажимаю на ячейку и передаю NSManagedObjectID в контроллер назначения в методе prepareForSegue.Очень случайно, когда я вносил изменения в детальный вид, извлеченный контроллер терял свой делегат или, как кажется, сам делегат, который является контроллером, освобождается.Вызывает сбой.

Выбранный контроллер результатов объявляется как свойство:

@property(nonatomic,strong) NSFetchedResultsController *fetchedResultsController;

Тогда вот как все работает, начиная с viewDidLoad.

- (void)viewDidLoad {
    [super viewDidLoad];
    [self loadDataAsynchronously];
}

-(void)loadDataAsynchronously {

    NSOperationQueue *queue = [NSOperationQueue new];
    NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self
                                                                            selector:@selector(loadData)
                                                                              object:nil];

    [queue addOperation:operation];
}


-(void)loadData {

    NSFetchRequest *findAllEntities = [[NSFetchRequest alloc] init];
    [findAllEntities setEntity:ENTITY_DESC];

    NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:@"created" ascending:YES];
    [findAllEntities setSortDescriptors:[NSArray arrayWithObject:sort]];
    [findAllEntities setFetchBatchSize:20];

    [NSFetchedResultsController deleteCacheWithName:@"MyCache"];
    if(self.fetchedResultsController==nil) {
        self.fetchedResultsController = [[NSFetchedResultsController alloc] 
                                            initWithFetchRequest:findAllPlants
                                            managedObjectContext:MOC
                                            sectionNameKeyPath:nil 
                                            cacheName:@"MyCache"];

        self.fetchedResultsController.delegate=self;
    }
    NSError *error=nil;
    if (![FRC performFetch:&error]) {
        exit(EXIT_FAILURE);
    }

    [self.dataTableView performSelectorOnMainThread:@selector(reloadData) withObject:nil waitUntilDone:YES];    

}

thisкод работает, и большую часть времени также работает в подробном представлении, которое называется следующим образом:

-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {

    IP2SegueIdentifier segueIdentifier = [IP2Factory segueSolver:[segue identifier]];  
    MyDestinationViewController *dvc = [segue destinationViewController];
    NSIndexPath *indexPath = [TV indexPathForSelectedRow];
    dvc.entityID=[[self.fetchedResultsController objectAtIndexPath: indexPath] objectID];

}

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

У меня мало сомнений на этом этапе, я использую iOS 5 и ARC, так что компилятор должен иметь (почти) полныйконтроль над методами освобождения и освобождения.И я также использую раскадровку с простой иерархией навигации, которая должна гарантировать, что вся предыдущая цепочка контроллеров представления будет сохранена.

Я также запустил профилировщик для анализа утечек памяти / зомби, но не смогВыясни, что что-то не так, наоборот, я был рад, что все управление объектами было в порядке.

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

спасибо

1 Ответ

21 голосов
/ 09 февраля 2012

Во-первых, заметка об ARC.В то время как ARC обеспечивает автоматическое обнуление указателей weak, оно не делает автоматическое обнуление assign указателей.NSFetchResultsController использует свойство assign для своего делегата (см. NSFetchedResultsController.h):

@property(nonatomic, assign) id< NSFetchedResultsControllerDelegate > delegate;

Вы все еще обязаны очистить себя в качестве делегата перед тем, как освободить.Обычно вы делаете это в dealloc:

- (void)dealloc {
  _fetchedResultsController.delegate = nil;
}

Вы также можете избавиться от своего fetchedResultsController в viewWillDisappear: (включая удаление себя в качестве делегата).Обычно вы не хотите, чтобы запросы выборки оставались рядом, когда вы находитесь за кадром.(Если вы это сделаете, вам, вероятно, следует управлять извлечением в объекте модели, а не в контроллере представления, поскольку контроллер представления может уйти в любое время, когда его представление находится вне экрана.)

Ваш loadData странен в этомон создает findAllEntities, но на самом деле использует findAllPlants.Была ли это опечатка или ошибка?Если в ivar есть отдельный запрос findAllPlants, это также может быть причиной ваших проблем.

...