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

View File

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