Originally created by @dylanmansur on GitHub (Aug 18, 2020).
I'm experiencing a crash when accessing a required string property from an entity. I haven't seen anything posted about it, so I'm guessing it could be an implementation issue but I'd appreciate some help finding the right direction to look.
Library version: 6.3.2 (We'll be moving to 7.2.0 in one of our next releases).
Relevant Stack trace:
closure #1 (CoreStore.CoreStoreManagedObject) -> A1 in CoreStore.ValueContainer.Required.value.getter : A1 Value.swift:155
partial apply forwarder for reabstraction thunk helper <A><A1 where A: CoreStore.CoreStoreObject, A1: CoreStore.ImportableAttributeType> from @callee_guaranteed (@guaranteed CoreStore.CoreStoreManagedObject) -> (@out A1?, @error @owned Swift.Error) to @escaping @callee_guaranteed (@in_guaranteed CoreStore.CoreStoreManagedObject) -> (@out A1?, @error @owned Swift.Error) <compiler-generated>:0
partial apply forwarder for reabstraction thunk helper <A><A1 where A: CoreStore.CoreStoreObject, A1: CoreStore.ImportableAttributeType> from @callee_guaranteed (@guaranteed CoreStore.CoreStoreManagedObject) -> (@out A1, @error @owned Swift.Error) to @escaping @callee_guaranteed (@in_guaranteed CoreStore.CoreStoreManagedObject) -> (@out A1, @error @owned Swift.Error)
libswiftCore.dylib
Swift.withExtendedLifetime<A, B>(A, (A) throws -> B) throws -> B
CoreStore.ValueContainer.Required.value.getter : A1 Value.swift:144
We're using a ListMonitor to populate a CollectionView.
It crashes force unwrapping the value in Value.swift ln. 155:
let token = Value.Required<String>("token", initial: "")
It's not something I've been able to reproduce, but we've seen a decent frequency of it happening. It doesn't seem to happen consistently for users, though (only around 1.5x per affected user).
My assumption would be that if the property was somehow missing, it would fail to insert or update the object.
Again, I'm fully expecting our implementation to be causing this, but if there's any areas you'd recommend investigating it would be much appreciated.
Originally created by @dylanmansur on GitHub (Aug 18, 2020).
I'm experiencing a crash when accessing a required string property from an entity. I haven't seen anything posted about it, so I'm guessing it could be an implementation issue but I'd appreciate some help finding the right direction to look.
Library version: 6.3.2 (We'll be moving to 7.2.0 in one of our next releases).
Relevant Stack trace:
```
closure #1 (CoreStore.CoreStoreManagedObject) -> A1 in CoreStore.ValueContainer.Required.value.getter : A1 Value.swift:155
partial apply forwarder for reabstraction thunk helper <A><A1 where A: CoreStore.CoreStoreObject, A1: CoreStore.ImportableAttributeType> from @callee_guaranteed (@guaranteed CoreStore.CoreStoreManagedObject) -> (@out A1?, @error @owned Swift.Error) to @escaping @callee_guaranteed (@in_guaranteed CoreStore.CoreStoreManagedObject) -> (@out A1?, @error @owned Swift.Error) <compiler-generated>:0
partial apply forwarder for reabstraction thunk helper <A><A1 where A: CoreStore.CoreStoreObject, A1: CoreStore.ImportableAttributeType> from @callee_guaranteed (@guaranteed CoreStore.CoreStoreManagedObject) -> (@out A1, @error @owned Swift.Error) to @escaping @callee_guaranteed (@in_guaranteed CoreStore.CoreStoreManagedObject) -> (@out A1, @error @owned Swift.Error)
libswiftCore.dylib
Swift.withExtendedLifetime<A, B>(A, (A) throws -> B) throws -> B
CoreStore.ValueContainer.Required.value.getter : A1 Value.swift:144
```
We're using a `ListMonitor` to populate a CollectionView.
It crashes force unwrapping the value in `Value.swift` ln. 155:
```
object.value(forKey: self.keyPath)! as! V.QueryableNativeType
```
The property in question is declared like so:
```
let token = Value.Required<String>("token", initial: "")
```
It's not something I've been able to reproduce, but we've seen a decent frequency of it happening. It doesn't seem to happen consistently for users, though (only around 1.5x per affected user).
My assumption would be that if the property was somehow missing, it would fail to insert or update the object.
Again, I'm fully expecting our implementation to be causing this, but if there's any areas you'd recommend investigating it would be much appreciated.
adam
added the question label 2025-12-29 15:29:24 +01:00
accessing an object already deleted from the store
Can you share some snippets how the token property is being used?
@JohnEstropia commented on GitHub (Aug 19, 2020):
Without any other info my guesses would be
1. a memory leak
2. accessing an object already deleted from the store
Can you share some snippets how the `token` property is being used?
Sure -
The class it is implemented in is more or less a UICollectionViewDatasource wrapper for a ListMonitor.
In that class, we set up the ListMonitor:
let recentResourceMonitor = CoreStore.monitorList(
From<RecentResourceEntity>()
.orderBy(.descending(\.lastOpenedAt))
)
And add ourself as an observer, conforming to ListObjectObserver and using the diffing methods to keep track of where we need to animate the UICollectionView updates. (I can post some of this, but there's nothing jumping out to me about it).
The RecentResourceEntity object has an optional relationship -- topic --- that has the property in question on it. The relationship is declared like so:
let topic = Relationship.ToOne<TopicEntity>("topic", inverse: { $0.recentResource }, deleteRule: .cascade)
The crash is happening when we're dequeueing a UICollectionViewCell in collectionView(_:cellForItemAt:). In that method, we're making struct versions of the models (the .detach() method returns the struct copy) to render the cells. It does this for any of its relationships if it finds an object on them:
let recentResourceEntity = self.recentResourceMonitor[indexPath.row]
let recentResource = recentResourceEntity.detach()
It's in the detach() method that the crash seems to always happen.
.
A memory leak seems like a possibility, but I'm unsure why it would surface so consistently in this spot. It's worth noting that the specific property that it happens on is not the first required property that it copies over in that class. I guess I was assuming that an object deletion would trigger on the first required property, but I'm not familiar enough with the codebase to rule it out.
It's on the first screen you see in the app, and seems to happen within a few seconds of the app launching. We have some migration logic happening on launch, but that property has always been required so I'm not sure if that is relevant.
@dylanmansur commented on GitHub (Aug 19, 2020):
Sure -
The class it is implemented in is more or less a `UICollectionViewDatasource` wrapper for a `ListMonitor`.
In that class, we set up the `ListMonitor`:
```
let recentResourceMonitor = CoreStore.monitorList(
From<RecentResourceEntity>()
.orderBy(.descending(\.lastOpenedAt))
)
```
And add ourself as an observer, conforming to `ListObjectObserver` and using the diffing methods to keep track of where we need to animate the `UICollectionView` updates. (I can post some of this, but there's nothing jumping out to me about it).
The `RecentResourceEntity` object has an optional relationship -- `topic` --- that has the property in question on it. The relationship is declared like so:
```
let topic = Relationship.ToOne<TopicEntity>("topic", inverse: { $0.recentResource }, deleteRule: .cascade)
```
The crash is happening when we're dequeueing a `UICollectionViewCell` in `collectionView(_:cellForItemAt:)`. In that method, we're making `struct` versions of the models (the `.detach()` method returns the `struct` copy) to render the cells. It does this for any of its relationships if it finds an object on them:
```
let recentResourceEntity = self.recentResourceMonitor[indexPath.row]
let recentResource = recentResourceEntity.detach()
```
It's in the `detach()` method that the crash seems to always happen.
.
A memory leak seems like a possibility, but I'm unsure why it would surface so consistently in this spot. It's worth noting that the specific property that it happens on is not the first required property that it copies over in that class. I guess I was assuming that an object deletion would trigger on the first required property, but I'm not familiar enough with the codebase to rule it out.
It's on the first screen you see in the app, and seems to happen within a few seconds of the app launching. We have some migration logic happening on launch, but that property has always been required so I'm not sure if that is relevant.
It looks like we wait to navigate to the relevant screen until we get a result with a success when creating the dataStack.
@dylanmansur commented on GitHub (Aug 19, 2020):
It looks like we wait to navigate to the relevant screen until we get a result with a success when creating the `dataStack`.
@JohnEstropia commented on GitHub (Aug 19, 2020):
May I see the `detach()` implementation here?
```swift
let recentResource = recentResourceEntity.detach()
```
We're not holding on to a reference to the source model or anything as far as I can tell.
@dylanmansur commented on GitHub (Aug 19, 2020):
Sure -
```
func detach() -> RecentResource {
return RecentResource(recentResourceEntity: self)
}
```
The init is just this. All the omitted lines are just copying over more properties:
```
struct RecentResource {
init(recentResourceEntity: RecentResourceEntity) {
self.id = recentResourceEntity.id.value
self.type = recentResourceEntity.type.value
...
self.topic = recentResourceEntity.topic.value?.detach()
...
}
}
```
Same thing with the topic:
```
func detach() -> Topic {
return Topic(topicEntity: self)
}
```
Again, all omitted values are just copying things over from the stored model. Mostly strings, ints, and bools.
```
struct Topic {
init(topicEntity: TopicEntity) {
self.id = topicEntity.id.value
self.updatedAt = topicEntity.updatedAt.value
...
self.token = topicEntity.token.value
...
}
}
```
We're not holding on to a reference to the source model or anything as far as I can tell.
There's nothing that stands out here. My guess still is that one of the objects being returned in the relationships are already deleted.
It's worth noting that the specific property that it happens on is not the first required property that it copies over in that class.
I'm not sure but it's possible that the other properties are just left cached somehow.
By the way, you posted the stack trace above but what was the final error message?
@JohnEstropia commented on GitHub (Aug 19, 2020):
There's nothing that stands out here. My guess still is that one of the objects being returned in the relationships are already deleted.
> It's worth noting that the specific property that it happens on is not the first required property that it copies over in that class.
I'm not sure but it's possible that the other properties are just left cached somehow.
By the way, you posted the stack trace above but what was the final error message?
@jimbengtsson92 commented on GitHub (Feb 16, 2023):
I have the exact same problem. This has started to happen when not using asynchronous-transactions when doing fetches at the same time as a different thread is deleting data. We can trigger this error in a unit test.
Should we be performing all fetches on a transaction instead of doing a fetch on the data store? If we do this the crash does not happen, but then we are not able to read data at the same time as we write, so the performance is not as good. Even if we apply a tweak-clause that sets $0.includesPendingChanges = false to the fetch this crash happens.
Please let me know if you need more information.
@jimbengtsson92 commented on GitHub (Feb 16, 2023):
I have the exact same problem. This has started to happen when not using [asynchronous-transactions](https://github.com/JohnEstropia/CoreStore#asynchronous-transactions) when doing fetches at the same time as a different thread is deleting data. We can trigger this error in a unit test.
Should we be performing all fetches on a transaction instead of doing a fetch on the data store? If we do this the crash does not happen, but then we are not able to read data at the same time as we write, so the performance is not as good. Even if we apply a [tweak-clause](https://github.com/JohnEstropia/CoreStore#tweak-clause) that sets `$0.includesPendingChanges = false` to the fetch this crash happens.
Please let me know if you need more information.
@jimbengtsson92 The best way to hold references to objects that have the possibility of getting deleted in the background is through the ObjectPublisher wrapper instead of the CoreStoreObject subclasses directly. I recommend you work with the CoreStoreObject instances directly only during transactions. At the very least, if you're passing them around different threads, make sure to fetch them from that thread before accessing any of their properties:
letobject:MyObject=trydataStack.fetchOne(....)// elsewheresomeQueue.async{withExtendedLifetime(dataStatck.beginUnsafe()){transactioninguardletobject:ObjectSnapshot<MyObject>=object.asSnapshot(in:transaction)else{return// object was deleted somewhere else}// snapshots are hard copies, so it's safe to access fields even if the object is deleted after this pointprint(object.$someField1)// always safeprint(object.$someField2)// always safe}}
Take note that CoreStoreObjects implement the ObjectRepresentation protocol, which gives you access to different wrappers depending on how you need to use the instances:
object.asPublisher(): returns an ObjectPublisher<T>, which is the recommended type for passing around a live instance. You can observe this instance for live changes.
object.asSnapshot(): returns an ObjectSnapshot<T>, which is a hard-copy of all field values (except relationships, which still need care when working with multithreads)
When you need to convert from one type to another between threads, these are also available:
object.asPublisher(in: dataStack): if somehow you have a background-thread instance you want to create an observable type from the main thread
object.asReadOnly(in dataStack): if somehow you have a background-thread instance you want to get a read-only instance from `DataStack's thread (i.e. the main thread)
object.asEditable(in: transaction): if you have an object you'd want to edit inside a transaction
object.asSnapshot(in: dataStack) and object.asSnapshot(in: transaction): create snapshots of an object coming from another thread
@JohnEstropia commented on GitHub (Feb 16, 2023):
@jimbengtsson92 The best way to hold references to objects that have the possibility of getting deleted in the background is through the `ObjectPublisher` wrapper instead of the `CoreStoreObject` subclasses directly. I recommend you work with the `CoreStoreObject` instances directly only during transactions. At the very least, if you're passing them around different threads, make sure to fetch them from that thread before accessing any of their properties:
```swift
let object: MyObject = try dataStack.fetchOne(....)
// elsewhere
someQueue.async {
withExtendedLifetime(dataStatck.beginUnsafe()) { transaction in
guard let object: ObjectSnapshot<MyObject> = object.asSnapshot(in: transaction) else {
return // object was deleted somewhere else
}
// snapshots are hard copies, so it's safe to access fields even if the object is deleted after this point
print(object.$someField1) // always safe
print(object.$someField2) // always safe
}
}
```
Take note that `CoreStoreObject`s implement the `ObjectRepresentation` protocol, which gives you access to different wrappers depending on how you need to use the instances:
- `object.asPublisher()`: returns an `ObjectPublisher<T>`, which is the recommended type for passing around a live instance. You can [observe this instance for live changes](https://github.com/JohnEstropia/CoreStore#objectpublisherreactive).
- `object.asSnapshot()`: returns an `ObjectSnapshot<T>`, which is a hard-copy of all field values (except relationships, which still need care when working with multithreads)
When you need to convert from one type to another between threads, these are also available:
- `object.asPublisher(in: dataStack)`: if somehow you have a background-thread instance you want to create an observable type from the main thread
- `object.asReadOnly(in dataStack)`: if somehow you have a background-thread instance you want to get a read-only instance from `DataStack's thread (i.e. the main thread)
- `object.asEditable(in: transaction)`: if you have an object you'd want to edit inside a transaction
- `object.asSnapshot(in: dataStack)` and `object.asSnapshot(in: transaction)`: create snapshots of an object coming from another thread
@jimbengtsson92 commented on GitHub (Feb 23, 2023):
@JohnEstropia I reverted my change so both all transactions and fetches are made on an AsynchronousDataTransaction and this fixed my problem.
However, I have seen the same crash when using CoreStore in both our WatchOS app and in its complication. I assume this is since they both use the same file to setup the SQLiteStore for the DataStack (which we want in order to be able to share the data between the watch and its complication), but I don't think this can be used as it is in a safe way since I think the transaction's background serial queue is not shared between the WatchOS extension's CoreStore and the complication's CoreStore. I don't think there is a way to use the same instance of the CoreStore between these 2, or I have at least not found a way to do it.
Do you think that also in this scenario it is best to use an ObjectPublisher<T>/ObjectSnapshot<T> in order to avoid this problem?
@jimbengtsson92 commented on GitHub (Feb 23, 2023):
@JohnEstropia I reverted my change so both all transactions **and** fetches are made on an `AsynchronousDataTransaction` and this fixed my problem.
However, I have seen the same crash when using `CoreStore` in both our WatchOS app and in its complication. I assume this is since they both use the same file to setup the `SQLiteStore` for the `DataStack` (which we want in order to be able to share the data between the watch and its complication), but I don't think this can be used as it is in a safe way since I think the `transaction`'s background serial queue is not shared between the WatchOS extension's `CoreStore` and the complication's `CoreStore`. I don't think there is a way to use the same instance of the `CoreStore` between these 2, or I have at least not found a way to do it.
Do you think that also in this scenario it is best to use an `ObjectPublisher<T>`/`ObjectSnapshot<T>` in order to avoid this problem?
@jimbengtsson92 commented on GitHub (Feb 23, 2023):
Edit: I probably missed the object property of a snapshot. It seems to work with snapshotWithRelationship.relationship.object?.asSnapshot(in: transaction) which gives me the snapshot.
And a follow up question now that I have started to look into using ObjectSnapshot<T> instead of CoreStoreObject.
object.asSnapshot(): returns an ObjectSnapshot<T>, which is a hard-copy of all field values (except relationships, which still need care when working with multithreads)
What is the best way to get the actual value of a Relationship.ToOne from an ObjectSnapshot<T>? When working with a CoreStoreObject I could simply use entityWithRelationship.relationship.value. Should I use snapshotWithRelationship.relationship.asSnapshot(in: dataStack)?.asReadOnly(in: dataStack)?
@jimbengtsson92 commented on GitHub (Feb 23, 2023):
Edit: I probably missed the `object` property of a snapshot. It seems to work with `snapshotWithRelationship.relationship.object?.asSnapshot(in: transaction)` which gives me the snapshot.
And a follow up question now that I have started to look into using `ObjectSnapshot<T>` instead of `CoreStoreObject`.
> * `object.asSnapshot()`: returns an `ObjectSnapshot<T>`, which is a hard-copy of all field values (except relationships, which still need care when working with multithreads)
What is the best way to get the actual value of a `Relationship.ToOne` from an `ObjectSnapshot<T>`? When working with a `CoreStoreObject` I could simply use `entityWithRelationship.relationship.value`. Should I use `snapshotWithRelationship.relationship.asSnapshot(in: dataStack)?.asReadOnly(in: dataStack)`?
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 @dylanmansur on GitHub (Aug 18, 2020).
I'm experiencing a crash when accessing a required string property from an entity. I haven't seen anything posted about it, so I'm guessing it could be an implementation issue but I'd appreciate some help finding the right direction to look.
Library version: 6.3.2 (We'll be moving to 7.2.0 in one of our next releases).
Relevant Stack trace:
We're using a
ListMonitorto populate a CollectionView.It crashes force unwrapping the value in
Value.swiftln. 155:The property in question is declared like so:
It's not something I've been able to reproduce, but we've seen a decent frequency of it happening. It doesn't seem to happen consistently for users, though (only around 1.5x per affected user).
My assumption would be that if the property was somehow missing, it would fail to insert or update the object.
Again, I'm fully expecting our implementation to be causing this, but if there's any areas you'd recommend investigating it would be much appreciated.
@JohnEstropia commented on GitHub (Aug 19, 2020):
Without any other info my guesses would be
Can you share some snippets how the
tokenproperty is being used?@dylanmansur commented on GitHub (Aug 19, 2020):
Sure -
The class it is implemented in is more or less a
UICollectionViewDatasourcewrapper for aListMonitor.In that class, we set up the
ListMonitor:And add ourself as an observer, conforming to
ListObjectObserverand using the diffing methods to keep track of where we need to animate theUICollectionViewupdates. (I can post some of this, but there's nothing jumping out to me about it).The
RecentResourceEntityobject has an optional relationship --topic--- that has the property in question on it. The relationship is declared like so:The crash is happening when we're dequeueing a
UICollectionViewCellincollectionView(_:cellForItemAt:). In that method, we're makingstructversions of the models (the.detach()method returns thestructcopy) to render the cells. It does this for any of its relationships if it finds an object on them:It's in the
detach()method that the crash seems to always happen..
A memory leak seems like a possibility, but I'm unsure why it would surface so consistently in this spot. It's worth noting that the specific property that it happens on is not the first required property that it copies over in that class. I guess I was assuming that an object deletion would trigger on the first required property, but I'm not familiar enough with the codebase to rule it out.
It's on the first screen you see in the app, and seems to happen within a few seconds of the app launching. We have some migration logic happening on launch, but that property has always been required so I'm not sure if that is relevant.
@JohnEstropia commented on GitHub (Aug 19, 2020):
Do you load your
ListMonitorbefore or after the migration?@dylanmansur commented on GitHub (Aug 19, 2020):
It looks like we wait to navigate to the relevant screen until we get a result with a success when creating the
dataStack.@JohnEstropia commented on GitHub (Aug 19, 2020):
May I see the
detach()implementation here?@dylanmansur commented on GitHub (Aug 19, 2020):
Sure -
The init is just this. All the omitted lines are just copying over more properties:
Same thing with the topic:
Again, all omitted values are just copying things over from the stored model. Mostly strings, ints, and bools.
We're not holding on to a reference to the source model or anything as far as I can tell.
@JohnEstropia commented on GitHub (Aug 19, 2020):
There's nothing that stands out here. My guess still is that one of the objects being returned in the relationships are already deleted.
By the way, you posted the stack trace above but what was the final error message?
@dylanmansur commented on GitHub (Aug 19, 2020):
Nothing is listed in the Organizer, but AppCenter is listing a
SIGTRAP.@jimbengtsson92 commented on GitHub (Feb 16, 2023):
I have the exact same problem. This has started to happen when not using asynchronous-transactions when doing fetches at the same time as a different thread is deleting data. We can trigger this error in a unit test.
Should we be performing all fetches on a transaction instead of doing a fetch on the data store? If we do this the crash does not happen, but then we are not able to read data at the same time as we write, so the performance is not as good. Even if we apply a tweak-clause that sets
$0.includesPendingChanges = falseto the fetch this crash happens.Please let me know if you need more information.
@JohnEstropia commented on GitHub (Feb 16, 2023):
@jimbengtsson92 The best way to hold references to objects that have the possibility of getting deleted in the background is through the
ObjectPublisherwrapper instead of theCoreStoreObjectsubclasses directly. I recommend you work with theCoreStoreObjectinstances directly only during transactions. At the very least, if you're passing them around different threads, make sure to fetch them from that thread before accessing any of their properties:Take note that
CoreStoreObjects implement theObjectRepresentationprotocol, which gives you access to different wrappers depending on how you need to use the instances:object.asPublisher(): returns anObjectPublisher<T>, which is the recommended type for passing around a live instance. You can observe this instance for live changes.object.asSnapshot(): returns anObjectSnapshot<T>, which is a hard-copy of all field values (except relationships, which still need care when working with multithreads)When you need to convert from one type to another between threads, these are also available:
object.asPublisher(in: dataStack): if somehow you have a background-thread instance you want to create an observable type from the main threadobject.asReadOnly(in dataStack): if somehow you have a background-thread instance you want to get a read-only instance from `DataStack's thread (i.e. the main thread)object.asEditable(in: transaction): if you have an object you'd want to edit inside a transactionobject.asSnapshot(in: dataStack)andobject.asSnapshot(in: transaction): create snapshots of an object coming from another thread@jimbengtsson92 commented on GitHub (Feb 16, 2023):
@JohnEstropia Thank you for the quick and good response 👍 I will try these out!
@jimbengtsson92 commented on GitHub (Feb 23, 2023):
@JohnEstropia I reverted my change so both all transactions and fetches are made on an
AsynchronousDataTransactionand this fixed my problem.However, I have seen the same crash when using
CoreStorein both our WatchOS app and in its complication. I assume this is since they both use the same file to setup theSQLiteStorefor theDataStack(which we want in order to be able to share the data between the watch and its complication), but I don't think this can be used as it is in a safe way since I think thetransaction's background serial queue is not shared between the WatchOS extension'sCoreStoreand the complication'sCoreStore. I don't think there is a way to use the same instance of theCoreStorebetween these 2, or I have at least not found a way to do it.Do you think that also in this scenario it is best to use an
ObjectPublisher<T>/ObjectSnapshot<T>in order to avoid this problem?@jimbengtsson92 commented on GitHub (Feb 23, 2023):
Edit: I probably missed the
objectproperty of a snapshot. It seems to work withsnapshotWithRelationship.relationship.object?.asSnapshot(in: transaction)which gives me the snapshot.And a follow up question now that I have started to look into using
ObjectSnapshot<T>instead ofCoreStoreObject.What is the best way to get the actual value of a
Relationship.ToOnefrom anObjectSnapshot<T>? When working with aCoreStoreObjectI could simply useentityWithRelationship.relationship.value. Should I usesnapshotWithRelationship.relationship.asSnapshot(in: dataStack)?.asReadOnly(in: dataStack)?