Originally created by @ujamshy on GitHub (Feb 5, 2018).
Hi, I need your help with a problem that causes our application to crash.
The crash is happening at customers` devices - we couldn't reproduce it on our tests
Architecture:
The relevant table in the database contains log records. LogManager contains a DataStack and uses it to create/insert/update/delete log records. LogObserver uses ListMonitor to get notifications about changes to the table and communicates these changes to a remote server.
It runs in its own serial queue and uses un UnsafeTransaction created from the DataStack to handle managed objects.
When it gets a notification about a new/updated object (didInsertObject/didUpdateObject) it performs the following code:
open func listMonitor(_ monitor: ListMonitor<M>, didInsertObject object: M, toIndexPath indexPath: IndexPath) {
self.localQueue.async {
// using its UnsafeTransaction to fetch the object
guard let existingObject = self.transaction.fetchExisting(object) else { return }
existingObject.refreshAndMerge()
// handle the object
self.handlePendingObject(existingObject)
}
}
LogObserver's transaction is created in its own queue the first time it is used:
lazy var transaction: UnsafeDataTransaction = {
let transaction = self.dataStack.beginUnsafe()
return transaction
}()
When handling an object we try to check if it is deleted by testing if its context is nil:
func handlePendingObject(_ object: M) {
...
// test if object was deleted
if object.managedObjectContext == nil {
return
}
}
Crash:
The crash happens sometimes when a record is deleted from the DataStack while still held by the LogObserver.
When printing the record all its attributes are displayed as nil (although they are not optionals).
When trying to access one of the attributes the application crashes with Exception Type: SIGTRAP.
Questions:
I have several questions:
Do we use correctly the UnsafeTransaction?
Is there a better test for deleted object?
Can you suggest a way to avoid the crash?
Thanks :)
Originally created by @ujamshy on GitHub (Feb 5, 2018).
Hi, I need your help with a problem that causes our application to crash.
The crash is happening at customers` devices - we couldn't reproduce it on our tests
**Architecture:**
The relevant table in the database contains log records.
**LogManager** contains a DataStack and uses it to create/insert/update/delete log records.
**LogObserver** uses ListMonitor to get notifications about changes to the table and communicates these changes to a remote server.
It runs in its own serial queue and uses un UnsafeTransaction created from the DataStack to handle managed objects.
When it gets a notification about a new/updated object (didInsertObject/didUpdateObject) it performs the following code:
```
open func listMonitor(_ monitor: ListMonitor<M>, didInsertObject object: M, toIndexPath indexPath: IndexPath) {
self.localQueue.async {
// using its UnsafeTransaction to fetch the object
guard let existingObject = self.transaction.fetchExisting(object) else { return }
existingObject.refreshAndMerge()
// handle the object
self.handlePendingObject(existingObject)
}
}
```
**LogObserver**'s transaction is created in its own queue the first time it is used:
```
lazy var transaction: UnsafeDataTransaction = {
let transaction = self.dataStack.beginUnsafe()
return transaction
}()
```
When handling an object we try to check if it is deleted by testing if its context is nil:
```
func handlePendingObject(_ object: M) {
...
// test if object was deleted
if object.managedObjectContext == nil {
return
}
}
```
**Crash**:
The crash happens sometimes when a record is deleted from the DataStack while still held by the LogObserver.
When printing the record all its attributes are displayed as nil (although they are not optionals).
When trying to access one of the attributes the application crashes with Exception Type: SIGTRAP.
**Questions**:
I have several questions:
1. Do we use correctly the UnsafeTransaction?
2. Is there a better test for deleted object?
3. Can you suggest a way to avoid the crash?
Thanks :)
adam
added the question label 2025-12-29 15:26:30 +01:00
CoreStore's transactions are designed more for updating data than observing for updates. There will be a hiccup with your DataStack-to-Transaction synchronization because of the changes propagating in different queues. My suggestion here would be to try using a ListMonitor created from the UnsafeDataTransaction and observe that directly.
That said, I haven't personally used a ListMonitor created from an UnsafeDataTransaction in my projects (although I know some users who had) so I cannot offer advices against existing pitfalls, if there are any.
If you have the luxury of redesigning your architecture, I strongly recommend you reduce the granularity of your transactions and to use perform(asynchronous:...) and perform(synchronous:) when you can. There is a reason that CoreStore's Async and Sync transactions execute serially: they protect you from synchronization issues like these by not having to worry about changes in parallel transactions.
@JohnEstropia commented on GitHub (Feb 8, 2018):
CoreStore's transactions are designed more for updating data than observing for updates. There will be a hiccup with your DataStack-to-Transaction synchronization because of the changes propagating in different queues. My suggestion here would be to try using a `ListMonitor` created from the `UnsafeDataTransaction` and observe that directly.
That said, I haven't personally used a `ListMonitor` created from an `UnsafeDataTransaction` in my projects (although I know some users who had) so I cannot offer advices against existing pitfalls, if there are any.
If you have the luxury of redesigning your architecture, I strongly recommend you reduce the granularity of your transactions and to use `perform(asynchronous:...)` and `perform(synchronous:)` when you can. There is a reason that CoreStore's Async and Sync transactions execute serially: they protect you from synchronization issues like these by not having to worry about changes in parallel transactions.
Let me refine my question. Suppose we have DataStack and UnsafeTransaction derived from the DataStack. Suppose also that an object in the DataStack and was added to the unsafe transaction using featchExisting. If the object is deleted from the DataStack should accessing it in the UnsafeTransaction cause exception?
@ujamshy commented on GitHub (Feb 8, 2018):
Let me refine my question. Suppose we have DataStack and UnsafeTransaction derived from the DataStack. Suppose also that an object in the DataStack and was added to the unsafe transaction using featchExisting. If the object is deleted from the DataStack should accessing it in the UnsafeTransaction cause exception?
I'm closing this issue as this is Core Data's behavior. Let me know if you find a resolution so we can put a warning somewhere in the README.
@JohnEstropia commented on GitHub (Apr 14, 2018):
I'm closing this issue as this is Core Data's behavior. Let me know if you find a resolution so we can put a warning somewhere in the README.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Originally created by @ujamshy on GitHub (Feb 5, 2018).
Hi, I need your help with a problem that causes our application to crash.
The crash is happening at customers` devices - we couldn't reproduce it on our tests
Architecture:
The relevant table in the database contains log records.
LogManager contains a DataStack and uses it to create/insert/update/delete log records.
LogObserver uses ListMonitor to get notifications about changes to the table and communicates these changes to a remote server.
It runs in its own serial queue and uses un UnsafeTransaction created from the DataStack to handle managed objects.
When it gets a notification about a new/updated object (didInsertObject/didUpdateObject) it performs the following code:
LogObserver's transaction is created in its own queue the first time it is used:
When handling an object we try to check if it is deleted by testing if its context is nil:
Crash:
The crash happens sometimes when a record is deleted from the DataStack while still held by the LogObserver.
When printing the record all its attributes are displayed as nil (although they are not optionals).
When trying to access one of the attributes the application crashes with Exception Type: SIGTRAP.
Questions:
I have several questions:
Thanks :)
@JohnEstropia commented on GitHub (Feb 8, 2018):
CoreStore's transactions are designed more for updating data than observing for updates. There will be a hiccup with your DataStack-to-Transaction synchronization because of the changes propagating in different queues. My suggestion here would be to try using a
ListMonitorcreated from theUnsafeDataTransactionand observe that directly.That said, I haven't personally used a
ListMonitorcreated from anUnsafeDataTransactionin my projects (although I know some users who had) so I cannot offer advices against existing pitfalls, if there are any.If you have the luxury of redesigning your architecture, I strongly recommend you reduce the granularity of your transactions and to use
perform(asynchronous:...)andperform(synchronous:)when you can. There is a reason that CoreStore's Async and Sync transactions execute serially: they protect you from synchronization issues like these by not having to worry about changes in parallel transactions.@ujamshy commented on GitHub (Feb 8, 2018):
Let me refine my question. Suppose we have DataStack and UnsafeTransaction derived from the DataStack. Suppose also that an object in the DataStack and was added to the unsafe transaction using featchExisting. If the object is deleted from the DataStack should accessing it in the UnsafeTransaction cause exception?
@JohnEstropia commented on GitHub (Feb 15, 2018):
@ujamshy That's more of Core Data's behavior than anything, and yes that may trigger an exception.
@JohnEstropia commented on GitHub (Apr 14, 2018):
I'm closing this issue as this is Core Data's behavior. Let me know if you find a resolution so we can put a warning somewhere in the README.