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)
}

В метода на did change object:

    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))
                }
            })
        )
    }
}

И накрая, в контролера did промени съдържанието метод:

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?

IDK.

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

Версия на отговора на Plot от 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, така че всички пътища на кода да са очевидни. Това е същият цялостен модел като кода на Plot.

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