2014-01-22 1 views
1

В моем приложении я использую CoreData для хранения и отображения данных с помощью NSFetchedResultsController.Как разбить основные потоки данных Data Data на слияние изменений?

Я следил за учебниками от raywenderlich, чтобы сделать это, и это много кода, но он работает нормально вообще - будет публиковать его части, когда это необходимо. Я застрял на одной проблеме, которую я не могу понять.

Данные, отображаемые внутри UITableView в сочетании с NSFetchedResultsController, могут быть обновлены в фоновом режиме - и вот здесь началась моя проблема.

Я делаю подход Pull-to-refresh и запускаю загрузку в фоновом режиме в отдельной теме. Он использует собственный NSManagedObjectContext, созданный для этого потока и сохраняющий его после создания всех объектов.

Вот код:

- (void)refresh:(id)sender 
{ 
[ServerConnection downloadContactsForToken:token success:^(NSDictionary* responseObject) 
    { 
     dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 
      NSManagedObjectContext* context = [[[AppManager sharedAppManager] createManagedObjectContext] retain]; 

      NSArray* responseContacts = responseObject[@"contacts"]; 
      [responseContacts enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { 
       //ContactDB is NSManagedObject 
       [[[ContactDB alloc] initWithDictionary:obj andContext:context] autorelease]; 
      }]; 

      [context save:nil]; 
      [context release]; 

      dispatch_async(dispatch_get_main_queue(), ^{ 
       [self.refreshControl endRefreshing]; 
      }); 
     }); 
    }]; 
} 

Согласно тому, что я прочитал в Apple, документы надлежащим образом, чтобы обнаружить эти изменения на основной нити NSManagedObjectContext заключается в следующем:

- (void) viewDidAppear:(BOOL)animated 
{ 
    [super viewDidAppear:animated]; 

    [[NSNotificationCenter defaultCenter] addObserver:self 
             selector:@selector(managedObjectContextDidSave:) 
              name:NSManagedObjectContextDidSaveNotification 
              object:nil]; 
} 

- (void)managedObjectContextDidSave:(NSNotification *)notification { 
    dispatch_async(dispatch_get_main_queue(), ^{ 
     if(notification.object != [[AppManager sharedAppManager] managedObjectContext]) { 
      [[[AppManager sharedAppManager] managedObjectContext] mergeChangesFromContextDidSaveNotification:notification]; 
     } 
    }); 
} 

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

Когда я делаю все на главной теме - он работает - они освобождаются, как ожидалось, при прокрутке UITableView.

Я нашел обходной путь, и я звоню:

[[[AppManager sharedAppManager] managedObjectContext] reset]; 

После слияния сделано:

[[[AppManager sharedAppManager] managedObjectContext] mergeChangesFromContextDidSaveNotification:notification]; 
[[[AppManager sharedAppManager] managedObjectContext] reset]; 

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

ответ

4

В общем, вы не должны делать ничего особенного с управляемыми объектами, кроме обычных правил управления памятью Foundation (подсчет ссылок). Поэтому просто убедитесь, что вы не сохранили их нигде, когда они вам не нужны.

Включение объектов банка в неисправности с использованием -refreshObject:mergeChanges: необходимо только тогда, когда вам нужно частично обрезать график объекта и по-прежнему иметь сильные ссылки на объекты.

И я заметил еще пару вещей в вашем коде.

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

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

+0

Ваши «другие вещи» хорошо соблюдены. Но, нужно ли нам частично обрезать граф объектов? Я думаю, что эта «обрезка» относится к освобождению объектов «жизни» без внешней ссылки и, таким образом, освобождению излишне занятой ОЗУ. И оперативная память всегда беспокоит мобильные устройства, поэтому нам нужно _allways_ посылки 'refreshObject: mergeChanges:' для того, чтобы вызвать сбои:/ – CouchDeveloper

+0

Некоторая полезная информация здесь, но обратите внимание, что ваши первые два абзаца ошибочны. У базовых данных абсолютно нет циклов, которые вам нужно знать и ломать. http://developer.apple.com/mac/library/documentation/cocoa/Conceptual/CoreData/Articles/cdMemory.html#//apple_ref/doc/uid/TP40001860-SW3 – memmons

+0

Вам нужно разбить его, только если вы все еще сохраняете эти объекты. Если вы этого не сделаете, Core Data выпустит данные, когда это будет важно, например, при предупреждении о низкой памяти. Если бы правила нормального управления памятью здесь не применимы, то с течением времени память будет расти бесконечно. См. Также комментарий Маркуса Зарры здесь: http://stackoverflow.com/a/3332177/1812858 – eofster

3

Это вызвано циклами сохранения. Очень часто при работе с управляемыми объектами. См. Core Data Programming Guide: Memory Management (Breaking Relationship Retain Cycles).

Когда у вас есть отношения между управляемыми объектами, каждый объект поддерживает сильную ссылку на объект или объекты, с которыми он связан. В среде управляемой памяти это вызывает цикл сохранения (см. Object Ownership and Disposal), который может предотвратить освобождение нежелательных объектов. Чтобы обеспечить сохранение циклов сохранения, когда вы закончите с объектом, вы можете использовать метод контекста управляемого объекта refreshObject:mergeChanges:, чтобы превратить его в неисправность.

Для обновления значений свойств управляемого объекта обычно используется refreshObject:mergeChanges:. Если флаг mergeChanges равен YES, метод объединяет значения свойства объекта с значениями объекта, доступного в координаторе постоянного хранилища. Однако, если флаг равен NO, метод просто превращает объект обратно в ошибку без слияния, что заставляет его выпускать связанные управляемые объекты. Это прерывает цикл сохранения между этим управляемым объектом и другими управляемыми объектами, которые он сохранил.

Также обратите внимание на контекст сохраняет сильные ссылки на управляемые объекты, которые имеют ожидающие изменения (вставки, делеция или обновление) до тех пор, контекст не посылается save:, reset, rollback или dealloc сообщения или соответствующее количество отмен для отмены изменения.

Кроме того, Core Data имеет концепцию «Пользовательское мероприятие». По умолчанию «Пользовательское событие» должным образом завернуто в цикле основного запуска, однако для MOC не в основном потоке вы несете ответственность за правильность обработки пользовательских событий. См. Use Thread Confinement to Support Concurrency и Track Changes in Other Threads Using Notifications.

+0

Итак, в какой момент я должен это сделать? До сих пор сохранение цикла между ними не имело особого значения, поскольку оно работает на одном потоке и когда я не делаю слияния между потоками. –

+0

Нужно ли это делать каждый раз, когда я загружаю данные из SQLLite, и я покончил с этим? Так в основном в моем случае каждый раз, когда я настраиваю ячейку? И как объединить это с объединением? –

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

  • Нет связанных вопросов^_^