added thread safety checks to ListMonitor to prevent deadlocks after calling refetch()

This commit is contained in:
John Estropia
2015-09-11 13:59:28 +09:00
parent 726e0eabe9
commit 656a99fe12
2 changed files with 92 additions and 7 deletions

View File

@@ -114,6 +114,11 @@ public final class ListMonitor<T: NSManagedObject> {
*/
public subscript(safeSectionIndex sectionIndex: Int, safeItemIndex itemIndex: Int) -> T? {
CoreStore.assert(
!self.isPendingRefetch || NSThread.isMainThread(),
"Attempted to access a \(typeName(self)) outside the main thread while a refetch is in progress."
)
guard let sections = self.fetchedResultsController.sections
where sectionIndex < sections.count else {
@@ -136,6 +141,11 @@ public final class ListMonitor<T: NSManagedObject> {
*/
public subscript(indexPath: NSIndexPath) -> T {
CoreStore.assert(
!self.isPendingRefetch || NSThread.isMainThread(),
"Attempted to access a \(typeName(self)) outside the main thread while a refetch is in progress."
)
return self.fetchedResultsController.objectAtIndexPath(indexPath) as! T
}
@@ -181,6 +191,11 @@ public final class ListMonitor<T: NSManagedObject> {
@warn_unused_result
public func objectsInAllSections() -> [T] {
CoreStore.assert(
!self.isPendingRefetch || NSThread.isMainThread(),
"Attempted to access a \(typeName(self)) outside the main thread while a refetch is in progress."
)
return (self.fetchedResultsController.fetchedObjects as? [T]) ?? []
}
@@ -193,6 +208,11 @@ public final class ListMonitor<T: NSManagedObject> {
@warn_unused_result
public func objectsInSection(section: Int) -> [T] {
CoreStore.assert(
!self.isPendingRefetch || NSThread.isMainThread(),
"Attempted to access a \(typeName(self)) outside the main thread while a refetch is in progress."
)
return (self.fetchedResultsController.sections?[section].objects as? [T]) ?? []
}
@@ -205,6 +225,11 @@ public final class ListMonitor<T: NSManagedObject> {
@warn_unused_result
public func objectsInSection(safeSectionIndex section: Int) -> [T]? {
CoreStore.assert(
!self.isPendingRefetch || NSThread.isMainThread(),
"Attempted to access a \(typeName(self)) outside the main thread while a refetch is in progress."
)
return (self.fetchedResultsController.sections?[section].objects as? [T]) ?? []
}
@@ -216,6 +241,11 @@ public final class ListMonitor<T: NSManagedObject> {
@warn_unused_result
public func numberOfSections() -> Int {
CoreStore.assert(
!self.isPendingRefetch || NSThread.isMainThread(),
"Attempted to access a \(typeName(self)) outside the main thread while a refetch is in progress."
)
return self.fetchedResultsController.sections?.count ?? 0
}
@@ -227,6 +257,11 @@ public final class ListMonitor<T: NSManagedObject> {
@warn_unused_result
public func numberOfObjects() -> Int {
CoreStore.assert(
!self.isPendingRefetch || NSThread.isMainThread(),
"Attempted to access a \(typeName(self)) outside the main thread while a refetch is in progress."
)
return self.fetchedResultsController.fetchedObjects?.count ?? 0
}
@@ -263,6 +298,11 @@ public final class ListMonitor<T: NSManagedObject> {
@warn_unused_result
public func sectionInfoAtIndex(section: Int) -> NSFetchedResultsSectionInfo {
CoreStore.assert(
!self.isPendingRefetch || NSThread.isMainThread(),
"Attempted to access a \(typeName(self)) outside the main thread while a refetch is in progress."
)
return self.fetchedResultsController.sections![section]
}
@@ -275,6 +315,11 @@ public final class ListMonitor<T: NSManagedObject> {
@warn_unused_result
public func sectionInfoAtIndex(safeSectionIndex section: Int) -> NSFetchedResultsSectionInfo? {
CoreStore.assert(
!self.isPendingRefetch || NSThread.isMainThread(),
"Attempted to access a \(typeName(self)) outside the main thread while a refetch is in progress."
)
guard let sections = self.fetchedResultsController.sections
where section < sections.count else {
@@ -293,6 +338,11 @@ public final class ListMonitor<T: NSManagedObject> {
@warn_unused_result
public func indexOf(object: T) -> Int? {
CoreStore.assert(
!self.isPendingRefetch || NSThread.isMainThread(),
"Attempted to access a \(typeName(self)) outside the main thread while a refetch is in progress."
)
return (self.fetchedResultsController.fetchedObjects as? [T] ?? []).indexOf(object)
}
@@ -305,6 +355,11 @@ public final class ListMonitor<T: NSManagedObject> {
@warn_unused_result
public func indexPathOf(object: T) -> NSIndexPath? {
CoreStore.assert(
!self.isPendingRefetch || NSThread.isMainThread(),
"Attempted to access a \(typeName(self)) outside the main thread while a refetch is in progress."
)
return self.fetchedResultsController.indexPathForObject(object)
}
@@ -766,6 +821,11 @@ public final class ListMonitor<T: NSManagedObject> {
*/
public func refetch(fetchClauses: [FetchClause]) {
CoreStore.assert(
NSThread.isMainThread(),
"Attempted to refetch a \(typeName(self)) outside the main thread."
)
self.isPendingRefetch = true
NSNotificationCenter.defaultCenter().postNotificationName(
@@ -780,19 +840,39 @@ public final class ListMonitor<T: NSManagedObject> {
return
}
strongSelf.fetchedResultsControllerDelegate.fetchedResultsController = nil
let fetchRequest = strongSelf.fetchedResultsController.fetchRequest
for clause in fetchClauses {
clause.applyToFetchRequest(fetchRequest)
}
try! strongSelf.fetchedResultsController.performFetch()
strongSelf.isPendingRefetch = false
NSNotificationCenter.defaultCenter().postNotificationName(
ListMonitorDidRefetchListNotification,
object: strongSelf
)
GCDQueue.Utility.async {
guard let strongSelf = self else {
return
}
try! strongSelf.fetchedResultsController.performFetch()
GCDQueue.Main.async { () -> Void in
guard let strongSelf = self else {
return
}
strongSelf.fetchedResultsControllerDelegate.fetchedResultsController = strongSelf.fetchedResultsController
strongSelf.isPendingRefetch = false
NSNotificationCenter.defaultCenter().postNotificationName(
ListMonitorDidRefetchListNotification,
object: strongSelf
)
}
}
}
}
@@ -808,6 +888,9 @@ public final class ListMonitor<T: NSManagedObject> {
fetchRequest.fetchLimit = 0
fetchRequest.resultType = .ManagedObjectResultType
fetchRequest.fetchBatchSize = 20
fetchRequest.includesPendingChanges = false
fetchRequest.shouldRefreshRefetchedObjects = true
for clause in fetchClauses {

View File

@@ -172,6 +172,8 @@ public final class ObjectMonitor<T: NSManagedObject> {
fetchRequest.fetchLimit = 0
fetchRequest.resultType = .ManagedObjectResultType
fetchRequest.sortDescriptors = []
fetchRequest.includesPendingChanges = false
fetchRequest.shouldRefreshRefetchedObjects = true
let originalObjectID = object.objectID
Where("SELF", isEqualTo: originalObjectID).applyToFetchRequest(fetchRequest)