The following simple function will cause Xcode 12E262 to have "Abort: trap 6" during compilation.
import UIKit
import CoreData
class ViewController: UIViewController {
func xyz() {
let container = NSPersistentContainer(name: "xyz")
let batchUpdateRequest = NSBatchUpdateRequest(entityName: "xyz")
let batchUpdateResult = try! container.viewContext.execute(batchUpdateRequest) as? NSBatchUpdateResult
guard let batchUpdateResult = batchUpdateResult else { return }
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
}
We will not observe "Abort: trap 6", if under Build Settings, we are using "Optimize for Speed" in Debug, instead of "No Optimization"
We can also avoid "Abort: trap 6", if we change the following code
guard let batchUpdateResult = batchUpdateResult else { return }
to
guard let batchUpdateResult2 = batchUpdateResult else { return }
May I know, why is it so?
A simpler code example to reproduce problem, without CoreData would be
import UIKit
class ViewController: UIViewController {
func getAny() throws -> Any? {
return nil
}
func xyz() {
let name = try! getAny() as? UIViewController
guard let name = name else { return }
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
}
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Created
I was wondering, when you use CoreData, do you usually create another equivalent struct based data class, to compliment the NSManagedObject?
The struct based data class, will act as the bridge, between UI layer, and CoreData data layer.
For instance, I have the following CoreData model data class.
@objc(NSTabInfo)
public class NSTabInfo: NSManagedObject {
}
extension NSTabInfo {
@nonobjc public class func fetchRequest() -> NSFetchRequest<NSTabInfo> {
return NSFetchRequest<NSTabInfo>(entityName: "NSTabInfo")
}
@NSManaged public var colorIndex: Int32
@NSManaged public var customColor: Int32
@NSManaged public var name: String?
@NSManaged public var order: Int64
@NSManaged public var typeValue: Int32
@NSManaged public var syncedTimestamp: Int64
@NSManaged public var uuid: UUID
}
extension NSTabInfo : Identifiable {
}
We need a UI, to represent such model data object. Initially, we have the following UI class
class TabInfoCell: UICollectionViewCell {
private var tabInfo: NSTabInfo?
func update(_ tabInfo: NSTabInfo) {
self.tabInfo = tabInfo
}
}
But, we just feel not comfortable with such design.
Letting TabInfoCell (UI class) to hold NSTabInfo (CoreData model data class) doesn't feel right, as NSTabInfo contains CoreData's context. I do not feel comfortable, to expose CoreData's context to an UI.
Will holding a class reference to NSTabInfo in UI, affect CoreData memory allocation/ deallocation strategy? Using weak reference might solve the issue. But, what should the UI do when the weak reference become nil?
With such concern, We have the following design
struct TabInfo {
enum Kind: Int {
case All = 0
case Calendar
case Custom
case Settings
}
let kind: Kind
var name: String?
var colorIndex: Int
var customColor: Int
var order: Int
var syncedTimestamp: Int64
var uuid: UUID
}
@objc(NSTabInfo)
public class NSTabInfo: NSManagedObject {
convenience init(context: NSManagedObjectContext, tabInfo: TabInfo) {
self.init(context: context)
self.colorIndex = Int32(tabInfo.colorIndex)
self.customColor = Int32(tabInfo.customColor)
self.name = tabInfo.name
self.order = Int64(tabInfo.order)
self.typeValue = Int32(tabInfo.kind.rawValue)
self.syncedTimestamp = tabInfo.syncedTimestamp
self.uuid = tabInfo.uuid
}
func toTabInfo() -> TabInfo {
return TabInfo(
kind: TabInfo.Kind(rawValue: Int(self.typeValue))!,
name: self.name,
colorIndex: Int(self.colorIndex),
customColor: Int(self.customColor),
order: Int(self.order),
syncedTimestamp: self.syncedTimestamp,
uuid: uuid
)
}
}
@nonobjc public class func fetchRequest() -> NSFetchRequest<NSTabInfo> {
return NSFetchRequest<NSTabInfo>(entityName: "NSTabInfo")
}
@NSManaged public var colorIndex: Int32
@NSManaged public var customColor: Int32
@NSManaged public var name: String?
@NSManaged public var order: Int64
@NSManaged public var typeValue: Int32
@NSManaged public var syncedTimestamp: Int64
@NSManaged public var uuid: UUID
}
extension NSTabInfo : Identifiable {
}
Then, in our UI class, it looks like
class TabInfoCell: UICollectionViewCell {
private var tabInfo: TabInfo?
func update(_ tabInfo: TabInfo) {
self.tabInfo = tabInfo
}
}
I was wondering, does such design philosophy make sense? Do you usually have a struct based data class, as the bridge between your UI layer, and the CoreData data layer?
Thanks.
This problem has buzzed me quite a while. So far, I still haven't founded a good solution.
Currently, I have an entity with a Bool column named pinned. I use it as the sectionNameKeyPath for NSFetchedResultsController.
So, my UICollectionView will always have 1 sections (All pinned = true, or All pinned = false), or 2 sections (Some pinned = true, and some pinned = false)
When I toggle the pinned value from true to false, or false to true, I expect FRC shall fire a "move" callback. (Due to item has moved to a new section) This happen most of the time.
However, sometimes, randomly, instead of firing "move" callback, FRC will fire "update" callback.
When such incident happen, I will exam the content of fetchedResultsController.sections. Then, I will notice the entity item stays in wrong section.
My FRC looks pretty straightforward.
lazy var fetchedResultsController: NSFetchedResultsControllerNSPlainNote = {
// Create a fetch request for the Quake entity sorted by time.
let fetchRequest = NSFetchRequestNSPlainNote(entityName: "NSPlainNote")
fetchRequest.sortDescriptors = [
NSSortDescriptor(key: "pinned", ascending: false)
]
// Create a fetched results controller and set its fetch request, context, and delegate.
let controller = NSFetchedResultsController(fetchRequest: fetchRequest,
managedObjectContext: CoreDataStack.INSTANCE.persistentContainer.viewContext,
sectionNameKeyPath: "pinned",
cacheName: nil
)
controller.delegate = fetchedResultsControllerDelegate
// Perform the fetch.
do {
try controller.performFetch()
} catch {
fatalError("Unresolved error \(error)")
}
return controller
}()
This is how I update the pinned column using background thread.
func updatePinned(_ objectID: NSManagedObjectID, _ pinned: Bool) {
let coreDataStack = CoreDataStack.INSTANCE
let backgroundContext = coreDataStack.backgroundContext
backgroundContext.perform {
let nsPlainNote = try! backgroundContext.existingObject(with: objectID) as! NSPlainNote
nsPlainNote.pinned = pinned
RepositoryUtils.saveContextIfPossible(backgroundContext)
}
}
I am not really sure, whether this can caused by my background thread.
As, if I replace the backgroundContext with viewContext, I haven't observed the random problem so far.
But, even so, I am not confident to conclude using backgroundContext is the culprit to this problem.
The setup of my background thread is also pretty straightforward. I cannot see how it can went wrong.
class CoreDataStack {
public static let INSTANCE = CoreDataStack()
private init() {
}
lazy var persistentContainer: NSPersistentContainer = {
let container = NSPersistentContainer(name: "xxx")
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
// TODO:
container.viewContext.automaticallyMergesChangesFromParent = true
//container.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
//container.viewContext.undoManager = nil
//container.viewContext.shouldDeleteInaccessibleFaults = true
return container
}()
lazy var backgroundContext: NSManagedObjectContext = {
let backgroundContext = persistentContainer.newBackgroundContext()
// TODO:
//backgroundContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
//backgroundContext.undoManager = nil
return backgroundContext
}()
}
I was wondering, have anyone of you encounter similar problem when trying to utilize sectionNameKeyPath or FRC? Do you know what? Do you have any workaround/ solution for that?
Thank you!
Apple has shown us how to perform heavy write operation using background thread (by using newBackgroundContext) in their official earth quake example - https://github.com/yccheok/earthquakes-WWDC20
But, what about heavy read operation? (millions of rows for stress test purpose)
We would also like our app UI to be responsiveness, when we are launching the app for first time, and the app is reading a large amount of data from CoreData.
The following is the code snippet which is using NSFetchedResultController.
[UI is not responsiveness if there are a lot of rows] }
[UI is not responsiveness if there are a lot of rows]
We try to perform controller.performFetch() using background thread. Still, but not sure why, the UI is still not responsiveness. My guess is that, after NSFetchedResultsController is occupied UI main thread, to perform some time consuming I/O read operation. }
[UI is responsive now. But the solution is incorrect...]
I guess, we need to place NSFetchedresultController under background thread too. Hence, we do the following modification. }
The UI is responsiveness during fetching, and data is able to be fetched and shown after some time.
However, if we investigate further by using the launching argument
com.apple.CoreData.ConcurrencyDebug 1
We will experience the following crash after controller.performFetch() finish executed.
CoreData`+[NSManagedObjectContext __Multithreading_Violation_AllThatIsLeftToUsIsHonor__]:
May I know, is it ever possible to make UI responsive, when we are using NSFetchedResultController to load a large amount of data (few millions rows as per testing)?
Can NSFetchedResultController ever operated under background thread?