NSFetchedResultsContollerDelegate для CollectionView

Я хотел бы использовать NSFetchedResultsControllerRelegate в CollectionViewController. Поэтому я просто изменил метод TableViewController для CollectionView.

(void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo
       atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type {

    switch(type) {
        case NSFetchedResultsChangeInsert:
            [self.collectionView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex]];
            break;

        case NSFetchedResultsChangeDelete:
            [self.collectionView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] ];

       break;
    }
}


(void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject
   atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type
  newIndexPath:(NSIndexPath *)newIndexPath {

  UICollectionView *collectionView = self.collectionView;

  switch(type) {

    case NSFetchedResultsChangeInsert:
        [collectionView insertItemsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]];
        break;

    case NSFetchedResultsChangeDelete:
        [collectionView deleteItemsAtIndexPaths:[NSArray arrayWithObject:indexPath]];
        break;

    case NSFetchedResultsChangeUpdate:
        [collectionView reloadItemsAtIndexPaths:[NSArray arrayWithObject:indexPath]];
        break;

    case NSFetchedResultsChangeMove:
        [collectionView deleteItemsAtIndexPaths:[NSArray arrayWithObject:indexPath]];
        [collectionView insertItemsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]];
        break;
  }
}

(void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
   [self.collectionView reloadData];
}

Но я не знаю, как обрабатывать WillChangeContent (beginUpdates для TableView) и DidChangeContent (endUpdates для TableView) для CollectionView.

Все работает нормально, кроме случаев, когда я перемещаю один элемент из одного раздела в другой. Тогда я получаю следующую ошибку.

Обычно это ошибка в наблюдателе NSManagedObjectContextObjectsDidChangeNotification. Неверное обновление: недопустимое количество элементов в разделе 0 ....

Есть идеи, как я могу решить эту проблему?


person aquarius68    schedule 12.12.2013    source источник


Ответы (6)


Комбинировать контроллер полученных результатов с представлением коллекции немного сложно. Проблема объясняется в

Если вы ищете, как обойти NSInternalInconsistencyException исключение времени выполнения с помощью UICollectionView, у меня есть пример на GitHub, в котором подробно описано, как поставить в очередь обновления из NSFetchedResultsControllerDelegate.

Проблема в том, что существующий класс UITableView использует beginUpdates и endUpdates для отправки пакетов в табличное представление. UICollectionView имеет новый метод performBatchUpdates:, который принимает параметр блока для обновления представления коллекции. Это сексуально, но это не работает с существующей парадигмой для NSFetchedResultsController.

К счастью, в этой статье также представлен пример реализации:

Из README:

Это пример того, как использовать новый UICollectionView с NSFetchedResultsController. Уловка состоит в том, чтобы поставить в очередь обновления, сделанные через NSFetchedResultsControllerDelegate, пока контроллер не завершит свои обновления. UICollectionView не имеет тех же beginUpdates и endUpdates, которые UITableView должны позволять ему легко работать с NSFetchedResultsController, поэтому вам нужно поставить их в очередь, иначе вы получите исключения времени выполнения внутренней согласованности.

person Martin R    schedule 12.12.2013
comment
Спасибо, Мартин. Я пробовал это заранее без обходного пути - не видел обновления для обходного пути. Теперь, когда в представлении коллекции есть обходной путь, он наконец-то работает. Благодаря тому, что у меня есть верхние и нижние колонтитулы, это мне очень помогло. Тем не менее, я надеюсь, что однажды эта ошибка будет решена. - person aquarius68; 13.12.2013
comment
@ aquarius68: На самом деле это не ошибка. Проблема в том, что методы делегата FRC и методы обновления представления коллекции не подходят друг другу. Исправление означало бы изменить или расширить один из API. - Но я рад, что у вас все получилось. - person Martin R; 13.12.2013
comment
Я больше не получаю сообщений об ошибках, но это еще не работает полностью; т.е. если пользователь добавляет первый элемент, он работает, но если пользователь добавляет второй элемент, он работает, только если я вернусь к табличному представлению, которое содержит объекты, связанные с объектами представления коллекции. - person aquarius68; 17.12.2013
comment
@ aquarius68: Кажется, это отдельная проблема, поэтому я предлагаю задать новый вопрос. Тогда больше людей прочитают это и, возможно, смогут помочь. - person Martin R; 17.12.2013
comment
Отличный материал! Наконец, проблема решена ... эту ошибку уже нужно устранить !! - person DogCoffee; 29.01.2014
comment
Репо сейчас пусто. Вы можете разместить код? Если возможно, Свифт? :) - person Daniel Springer; 26.06.2018
comment
@DaniSpringer: в репозитории есть ссылка на github.com/jessesquires/JSQDataSourcesKit, и есть решения Swift. в других ответах вы пробовали один из них? - person Martin R; 26.06.2018
comment
@MartinR, еще один замечательный ответ, спасибо. Я поставил обновленную версию внизу. Но обратите внимание на вопрос, который я задаю в конце - я просто не знаю: O - person Fattie; 03.02.2020

Вот моя реализация на Swift. Сначала инициализируйте массив NSBlockOperations:

var blockOperations: [NSBlockOperation] = []

В контроллере изменится, повторно инициализируйте массив:

func controllerWillChangeContent(controller: NSFetchedResultsController) {
    blockOperations.removeAll(keepCapacity: false)
}

В методе объекта изменения:

    func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) {

    if type == NSFetchedResultsChangeType.Insert {
        println("Insert Object: \(newIndexPath)")

        blockOperations.append(
            NSBlockOperation(block: { [weak self] in
                if let this = self {
                    this.collectionView!.insertItemsAtIndexPaths([newIndexPath!])
                }
            })
        )
    }
    else if type == NSFetchedResultsChangeType.Update {
        println("Update Object: \(indexPath)")
        blockOperations.append(
            NSBlockOperation(block: { [weak self] in
                if let this = self {
                    this.collectionView!.reloadItemsAtIndexPaths([indexPath!])
                }
            })
        )
    }
    else if type == NSFetchedResultsChangeType.Move {
        println("Move Object: \(indexPath)")

        blockOperations.append(
            NSBlockOperation(block: { [weak self] in
                if let this = self {
                    this.collectionView!.moveItemAtIndexPath(indexPath!, toIndexPath: newIndexPath!)
                }
            })
        )
    }
    else if type == NSFetchedResultsChangeType.Delete {
        println("Delete Object: \(indexPath)")

        blockOperations.append(
            NSBlockOperation(block: { [weak self] in
                if let this = self {
                    this.collectionView!.deleteItemsAtIndexPaths([indexPath!])
                }
            })
        )
    }
}

В методе раздела "Сделано изменение":

func controller(controller: NSFetchedResultsController, didChangeSection sectionInfo: NSFetchedResultsSectionInfo, atIndex sectionIndex: Int, forChangeType type: NSFetchedResultsChangeType) {

    if type == NSFetchedResultsChangeType.Insert {
        println("Insert Section: \(sectionIndex)")

        blockOperations.append(
            NSBlockOperation(block: { [weak self] in
                if let this = self {
                    this.collectionView!.insertSections(NSIndexSet(index: sectionIndex))
                }
            })
        )
    }
    else if type == NSFetchedResultsChangeType.Update {
        println("Update Section: \(sectionIndex)")
        blockOperations.append(
            NSBlockOperation(block: { [weak self] in
                if let this = self {
                    this.collectionView!.reloadSections(NSIndexSet(index: sectionIndex))
                }
            })
        )
    }
    else if type == NSFetchedResultsChangeType.Delete {
        println("Delete Section: \(sectionIndex)")

        blockOperations.append(
            NSBlockOperation(block: { [weak self] in
                if let this = self {
                    this.collectionView!.deleteSections(NSIndexSet(index: sectionIndex))
                }
            })
        )
    }
}

И, наконец, в контроллере действительно изменился метод содержимого:

func controllerDidChangeContent(controller: NSFetchedResultsController) {        
    collectionView!.performBatchUpdates({ () -> Void in
        for operation: NSBlockOperation in self.blockOperations {
            operation.start()
        }
    }, completion: { (finished) -> Void in
        self.blockOperations.removeAll(keepCapacity: false)
    })
}

Я лично добавил код в метод deinit, чтобы отменить операции, когда ViewController вот-вот будет освобожден:

deinit {
    // Cancel all block operations when VC deallocates
    for operation: NSBlockOperation in blockOperations {
        operation.cancel()
    }

    blockOperations.removeAll(keepCapacity: false)
}
person Plot    schedule 05.03.2015
comment
Нет .Move изменить тип для разделов? - person pkamb; 11.06.2016
comment
@pkamb нет перемещения для разделов в UICollectionView. - person Fogmeister; 09.12.2016

Я сделал решение @ Plot это собственный объект и преобразовал его в Swift 2.

import Foundation
import CoreData

class CollectionViewFetchedResultsControllerDelegate: NSObject, NSFetchedResultsControllerDelegate {

    // MARK: Properties

    private let collectionView: UICollectionView
    private var blockOperations: [NSBlockOperation] = []

    // MARK: Init

    init(collectionView: UICollectionView) {
        self.collectionView = collectionView
    }

    // MARK: Deinit

    deinit {
        blockOperations.forEach { $0.cancel() }
        blockOperations.removeAll(keepCapacity: false)
    }

    // MARK: NSFetchedResultsControllerDelegate

    func controllerWillChangeContent(controller: NSFetchedResultsController) {
        blockOperations.removeAll(keepCapacity: false)
    }

    func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) {

        switch type {

        case .Insert:
            guard let newIndexPath = newIndexPath else { return }
            let op = NSBlockOperation { [weak self] in self?.collectionView.insertItemsAtIndexPaths([newIndexPath]) }
            blockOperations.append(op)

        case .Update:
            guard let newIndexPath = newIndexPath else { return }
            let op = NSBlockOperation { [weak self] in self?.collectionView.reloadItemsAtIndexPaths([newIndexPath]) }
            blockOperations.append(op)

        case .Move:
            guard let indexPath = indexPath else { return }
            guard let newIndexPath = newIndexPath else { return }
            let op = NSBlockOperation { [weak self] in self?.collectionView.moveItemAtIndexPath(indexPath, toIndexPath: newIndexPath) }
            blockOperations.append(op)

        case .Delete:
            guard let indexPath = indexPath else { return }
            let op = NSBlockOperation { [weak self] in self?.collectionView.deleteItemsAtIndexPaths([indexPath]) }
            blockOperations.append(op)

        }
    }

    func controller(controller: NSFetchedResultsController, didChangeSection sectionInfo: NSFetchedResultsSectionInfo, atIndex sectionIndex: Int, forChangeType type: NSFetchedResultsChangeType) {

        switch type {

        case .Insert:
            let op = NSBlockOperation { [weak self] in self?.collectionView.insertSections(NSIndexSet(index: sectionIndex)) }
            blockOperations.append(op)

        case .Update:
            let op = NSBlockOperation { [weak self] in self?.collectionView.reloadSections(NSIndexSet(index: sectionIndex)) }
            blockOperations.append(op)

        case .Delete:
            let op = NSBlockOperation { [weak self] in self?.collectionView.deleteSections(NSIndexSet(index: sectionIndex)) }
            blockOperations.append(op)

        default: break

        }
    }

    func controllerDidChangeContent(controller: NSFetchedResultsController) {
        collectionView.performBatchUpdates({
            self.blockOperations.forEach { $0.start() }
        }, completion: { finished in
            self.blockOperations.removeAll(keepCapacity: false)
        })
    }

}

Использование:

fetchedResultsController.delegate = CollectionViewFetchedResultsControllerDelegate(collectionView)

Версия Swift 4

private var blockOperations: [BlockOperation] = []

func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
    blockOperations.removeAll(keepingCapacity: false)
}

func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>,
                didChange anObject: Any,
                at indexPath: IndexPath?,
                for type: NSFetchedResultsChangeType,
                newIndexPath: IndexPath?) {

    let op: BlockOperation
    switch type {
    case .insert:
        guard let newIndexPath = newIndexPath else { return }
        op = BlockOperation { self.collectionView.insertItems(at: [newIndexPath]) }

    case .delete:
        guard let indexPath = indexPath else { return }
        op = BlockOperation { self.collectionView.deleteItems(at: [indexPath]) }
    case .move:
        guard let indexPath = indexPath,  let newIndexPath = newIndexPath else { return }
        op = BlockOperation { self.collectionView.moveItem(at: indexPath, to: newIndexPath) }
    case .update:
        guard let indexPath = indexPath else { return }
        op = BlockOperation { self.collectionView.reloadItems(at: [indexPath]) }
    }

    blockOperations.append(op)
}

func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
    collectionView.performBatchUpdates({
        self.blockOperations.forEach { $0.start() }
    }, completion: { finished in
        self.blockOperations.removeAll(keepingCapacity: false)
    })
}
person Adam Waite    schedule 09.01.2016
comment
это выдает ошибку Extensions must not contain stored properties на private var blockOperations: [BlockOperation] = [] Я не знаю почему = \ - person Basil; 24.11.2018
comment
Я не пробовал, но мне интересно. Это работает: fetchedResultsController.delegate = CollectionViewFetchedResultsControllerDelegate(collectionView)? Я полагаю, delegate это weak. - person shallowThought; 06.12.2019

Версия на 2020 год:

Основываясь на невероятных ответах выше и что соответствует знакомому примеру Apple для таблиц:

Рассмотрим знакомый пример Apple для табличных представлений:

https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/CoreData/nsfetchedresultscontroller.html#//apple_ref/doc/uid/TP40001075-CH8-SW1

в заголовке

Передача изменений данных в табличное представление ...

So,

func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
    switch type {
    case .insert:
        insertRows(at: [newIndexPath!], with: .fade)
    case .delete:
        deleteRows(at: [indexPath!], with: .fade)
    case .update:
        reloadRows(at: [indexPath!], with: .fade)
    case .move:
        moveRow(at: indexPath!, to: newIndexPath!)
    }
}

.

Вот аналогичный шаблон для копирования и вставки для представлений коллекции с текущим синтаксисом и т. Д.

var ops: [BlockOperation] = []

func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
    switch type {
        case .insert:
            ops.append(BlockOperation(block: { [weak self] in
                self?.insertItems(at: [newIndexPath!])
            }))
        case .delete:
            ops.append(BlockOperation(block: { [weak self] in
                self?.deleteItems(at: [indexPath!])
            }))
        case .update:
            ops.append(BlockOperation(block: { [weak self] in
                self?.reloadItems(at: [indexPath!])
            }))
        case .move:
            ops.append(BlockOperation(block: { [weak self] in
                self?.moveItem(at: indexPath!, to: newIndexPath!)
            }))
        @unknown default:
            break
    }
}

func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
    performBatchUpdates({ () -> Void in
        for op: BlockOperation in self.ops { op.start() }
    }, completion: { (finished) -> Void in self.ops.removeAll() })
}

deinit {
    for o in ops { o.cancel() }
    ops.removeAll()
}

.

(Я только что пропустил материал разделов, это то же самое.)

Ничего не делать в controllerWillChangeContent?

В великолепном ответе @PhuahYeeKeat в controllerWillChangeContent очищается массив ops. Я могу ошибаться, но для этого нет причин; он надежно опорожняется циклом пакетных обновлений. Просто ничего не делайте в controllerWillChangeContent.

Есть гонка?

Меня беспокоит, что произойдет, если появится новый didChange пока performBatchUpdates обрабатывает предыдущий пакет.

Я действительно не знаю, делает ли performBatchUpdates локальную копию или что - в таком случае, глобальную копию следует удалить перед выполнением performBatchUpdates?

Я НЕ ЗНАЮ.

person Fattie    schedule 03.02.2020
comment
Это отлично работает на iOS 13, в то время как другие решения, найденные в Интернете (например, эта суть, популярный результат Google) заставил мой пользовательский интерфейс зависать во время важных операций, таких как синхронизация iCloud на новом устройстве. Я не вижу здесь этих зависаний, по-видимому, из-за того, как обрабатываются BlockOperations (я не эксперт по параллелизму, но я сообщу о проблемах, если найду их, потому что CollectionView с FetchedResultsController на удивление не хватает хороших, обновленных iOS 13 ресурсов, кроме этот ответ). Спасибо, @Fattie! - person cdf1982; 27.07.2020
comment
@ cdf1982 - спасибо, да, вы совершенно правы. разработка этого решения стоила нам УДАЧИ времени ... действительно, чтобы решить именно те проблемы, которые вы наметили. И да, это отличный пример того, где пример кода, который вы часто видите в Интернете, к сожалению, полностью неверен. Поистине странный уголок Apple, что они не встроили решение для представлений коллекций в свой главный продукт CoreData. они дают решение дрянным старым представлениям таблиц, но не представлениям коллекций! Это одна из странностей Apple! Надеюсь, они скоро это сделают. - person Fattie; 27.07.2020
comment
Я пробовал этот код. Я получаю исключение: попытка вставить элемент 0 в раздел 0, но после обновления collectionView.performBatchUpdates внутри функции didChangeContent в разделе 0 осталось только 0 элементов. - person Vangola; 03.08.2020
comment
можно продолжить только стандартную отладку, добавьте повсюду операторы печати, чтобы найти проблему - person Fattie; 04.08.2020
comment
Редакторы: отличная работа, я просто добавил недостающий тег Swift, который исправит всю страницу - person Fattie; 24.04.2021

Версия ответа на сюжет 2019 года:

var blockOperations: [BlockOperation] = []

func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
    blockOperations.removeAll(keepingCapacity: false)
}

func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
    if type == NSFetchedResultsChangeType.insert {
        print("Insert Object: \(newIndexPath)")

        blockOperations.append(
            BlockOperation(block: { [weak self] in
                if let this = self {
                    this.collectionView!.insertItems(at: [newIndexPath!])
                }
            })
        )
    }
    else if type == NSFetchedResultsChangeType.update {
        print("Update Object: \(indexPath)")
        blockOperations.append(
            BlockOperation(block: { [weak self] in
                if let this = self {
                    this.collectionView!.reloadItems(at: [indexPath!])
                }
            })
        )
    }
    else if type == NSFetchedResultsChangeType.move {
        print("Move Object: \(indexPath)")

        blockOperations.append(
            BlockOperation(block: { [weak self] in
                if let this = self {
                    this.collectionView!.moveItem(at: indexPath!, to: newIndexPath!)
                }
            })
        )
    }
    else if type == NSFetchedResultsChangeType.delete {
        print("Delete Object: \(indexPath)")

        blockOperations.append(
            BlockOperation(block: { [weak self] in
                if let this = self {
                    this.collectionView!.deleteItems(at: [indexPath!])
                }
            })
        )
    }
}

func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange sectionInfo: NSFetchedResultsSectionInfo, atSectionIndex sectionIndex: Int, for type: NSFetchedResultsChangeType) {
    if type == NSFetchedResultsChangeType.insert {
        print("Insert Section: \(sectionIndex)")

        blockOperations.append(
            BlockOperation(block: { [weak self] in
                if let this = self {
                    this.collectionView!.insertSections(IndexSet(integer: sectionIndex))
                }
            })
        )
    }
    else if type == NSFetchedResultsChangeType.update {
        print("Update Section: \(sectionIndex)")
        blockOperations.append(
            BlockOperation(block: { [weak self] in
                if let this = self {
                    this.collectionView!.reloadSections(IndexSet(integer: sectionIndex))
                }
            })
        )
    }
    else if type == NSFetchedResultsChangeType.delete {
        print("Delete Section: \(sectionIndex)")

        blockOperations.append(
            BlockOperation(block: { [weak self] in
                if let this = self {
                    this.collectionView!.deleteSections(IndexSet(integer: sectionIndex))
                }
            })
        )
    }
}

func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
    collectionView!.performBatchUpdates({ () -> Void in
        for operation: BlockOperation in self.blockOperations {
            operation.start()
        }
    }, completion: { (finished) -> Void in
        self.blockOperations.removeAll(keepingCapacity: false)
    })
}

deinit {
    // Cancel all block operations when VC deallocates
    for operation: BlockOperation in blockOperations {
        operation.cancel()
    }

    blockOperations.removeAll(keepingCapacity: false)
}
person Phuah Yee Keat    schedule 21.09.2019

Вот немного Swift, который работает с installsStandardGestureForInteractiveMovement UICollectionViewController и несколько подсушен и включает installsStandardGestureForInteractiveMovement, так что все пути кода очевидны. Это тот же общий шаблон, что и код участка.

var fetchedResultsProcessingOperations: [NSBlockOperation] = []

private func addFetchedResultsProcessingBlock(processingBlock:(Void)->Void) {
    fetchedResultsProcessingOperations.append(NSBlockOperation(block: processingBlock))
}

func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) {

    switch type {
    case .Insert:
        addFetchedResultsProcessingBlock {self.collectionView!.insertItemsAtIndexPaths([newIndexPath!])}
    case .Update:
        addFetchedResultsProcessingBlock {self.collectionView!.reloadItemsAtIndexPaths([indexPath!])}
    case .Move:
        addFetchedResultsProcessingBlock {
            // If installsStandardGestureForInteractiveMovement is on
            // the UICollectionViewController will handle this on its own.
            guard !self.installsStandardGestureForInteractiveMovement else {
                return
            }
            self.collectionView!.moveItemAtIndexPath(indexPath!, toIndexPath: newIndexPath!)
        }
    case .Delete:
        addFetchedResultsProcessingBlock {self.collectionView!.deleteItemsAtIndexPaths([indexPath!])}
    }

}

func controller(controller: NSFetchedResultsController, didChangeSection sectionInfo: NSFetchedResultsSectionInfo, atIndex sectionIndex: Int, forChangeType type: NSFetchedResultsChangeType) {

    switch type {
    case .Insert:
        addFetchedResultsProcessingBlock {self.collectionView!.insertSections(NSIndexSet(index: sectionIndex))}
    case .Update:
        addFetchedResultsProcessingBlock {self.collectionView!.reloadSections(NSIndexSet(index: sectionIndex))}
    case .Delete:
        addFetchedResultsProcessingBlock {self.collectionView!.deleteSections(NSIndexSet(index: sectionIndex))}
    case .Move:
        // Not something I'm worrying about right now.
        break
    }

}

func controllerDidChangeContent(controller: NSFetchedResultsController) {
    collectionView!.performBatchUpdates({ () -> Void in
        for operation in self.fetchedResultsProcessingOperations {
            operation.start()
        }
        }, completion: { (finished) -> Void in
            self.fetchedResultsProcessingOperations.removeAll(keepCapacity: false)
    })
}

deinit {
    for operation in fetchedResultsProcessingOperations {
        operation.cancel()
    }

    fetchedResultsProcessingOperations.removeAll()
}
person Jonathan Zhan    schedule 05.11.2015