It's not an issue, but rather just a general question on how realm collection change notifications are dispatched if changes happen in background thread.
So there is a scenario (in Realm Cocoa):
RLMResults of objects of class Foo: RLMObject is fetched from default realm. The list is then transformed into array [Foo] and saved as variable in view controller (does not matter)
Table displays that list of foo objects
Some objects are deleted on background thread
User scrolls the table view, cell is reused, object from array at index x is accessed, but it was deleted on background thread and crash happens, because object was deleted or invalidated. As expected.
To solve that we could use collection notifications and refresh the list when changes occur. As I tried everything works as expected, but isn't it possible that dispatch of cell reuse will occur before notification is dispatched on main thread so that cell setup method will be using invalidated object?
Just tried to explain the question as detailed as possible.
Mainly the question is about situation(s) when data sync with server (not using Realm's mobile platform) is happening on background thread and views, whether they're table cells or any other views, are holding references to could-be-deleted objects. Is it a good practice to check if object was invalidated when trying to do something with the object because it could be deleted on background thread.
I see a couple of solutions:
each time accessing reference of RLMObject subclass object check if it is not invalidated
wrap the object into view model (leaving all the good parts of self-updating model features) which then leaves with another two solutions when trying to change the model:
save object id in view model so that when trying to change it would be possible to fetch object again
have a reference to the object itself having the same problems as the first point
What are the suggestions for this?
EDIT:
Using Results and List sometimes not really possible if the object Foo is complicated. For example when opening details screen of Foo table view with lots of different cells accessing properties of Foo class' object. So on notification about deleted object screen could be dismissed, but as user scrolls isn't it possible that cell could be accessing invalidated object?
Maybe the question is just stupid, over engineered about raise conditions, but I'm curious if it's possible that dispatches on main thread will occur something like: object becomes invalidated (I don't know how that happens in realm internally> then some code which access that object then notification about invalidated object
The list is then transformed into array [Foo] and saved as variable in view controller (does not matter)
Why do you do this? This forces every object in the Results to be materialized into memory, with (potentially expensive) Swift-level object accessors to be created and some db data read from disk. All Realm collections conform to the Swift standard library's CollectionType protocol, meaning that it behaves like other collections such as Array already. Copying out all of the Results elements into an Array will also mean that the array will quickly get out of sync with its underlying data, since Array isn't auto-updating unlike Results.
This easily explains why some of the Realm objects that you copy into the Array are then deleted, which causes the objects to become invalidated, meaning that any subsequent access is a violation of Realm's API.
Long story short, don't copy Results, List or any other Realm collection into an Array.
Related
Apple describes faults as partially materialised futures which is very similar to the term partially materialised views used in several databases. Word partial means for me that some field of this object are initialised (realised) and some not. But Apple describes faults as:
A managed object fault is an instance of the appropriate class, but its persistent variables are not yet initialized.
Are there any way to partially realise a fault? E.g. I have object with 10 field and I want a partial object with only two fields realised while the whole object is still a fault (hence all other fields are still nil). I'm not talking about propertiesToFetch of NSFetchRequest here but about so called partial faults.
Because currently when fault is fired all properties are realised. Here is a quote from the docs:
If you access a property on the Department object — its name, for example — the fault fires and Core Data executes a fetch for you to retrieve all of the object's attributes
How can I customise fault realisation?
I don't believe that there is anyway to do this. When core data loads a managedObject it loads everything. You can see in NSMangedObject's interface that it has a single property of isFault which is either true of false.
If you have a property that is very large (a blob of data for example) and don't want to load it unless it is needed, then I would suggest storing it as a separate entity with a relationship. This way it will only be loaded (faulted) when you request the property.
I have an issue where if I make changes to a core data object, save then refreshing the object, causes my NSFetchedResultsController to show a duplicate object. I think I understand what's going on, but I'm looking for someone to confirm, and also to hopefully give some more detail as to why.
To explain in more dtail I have two entities, Fixture and Position. A Fixture has many Positions, and a Position belongs to only one Fixture. To reproduce the issue I do the following:
Fetch all Positions.
Modify some value (any one) on that objects Fixture. I.E foo.fixture.name = "foobar"
Save the context
Refresh objects by calling context.refreshAllObjects, or context.refreshObject(foo, mergeChanges: false/true).
I have a tableview using a fetched results controller which displays Fixures. After doing the above the tableview will display duplicates for each item (it doesn't matter if I use the delegate methods of the FRC to do the update or I just reload the tableview).
It appears what's happening is that the refresh invalidates the objects that the FRC knows about, while at the same time gets knowledge of another set of objects. If, as step #5, I call frc.performFetch() then the problem goes away.
Other things to note:
No matter how many times I run the code I only get two of each object (I'm using a random button to trigger it for testing).
init(entityName, context) is called on my Fixture subclass as soon as I access the Fixture property of my object during the next code run (i.e after refresh was called).
In my sample everything is taking place on the same context (though it happens with child contexts as well)
To give some more context as to how I got myself in this situation in the first place users can click on a fixture in the list and then ultimately narrow down on a single position a few screens later where they can perform actions that modify the fixture. There are other active areas of the application at this point that are listening to the NSManagedObjectContextDidSaveNotification and I want them to update their objects so they can display the correct data, which is why I was calling refresh.
I've dug around in the docs and I can't see anything that specifically explains my theory that updating the context causes NSFetchedResultsController to have invalid objects. Can anyone shed some light on this behavior?
First, you really should not be overriding -init... on a NSManagedObject. That is one of the methods that you are strongly discouraged from overriding and can very easily be the source of your issues.
Second, your entire UI should be using a single instance of the NSManagedObjectContext that is associated to the main queue and therefore there should only be ONE instance of any particular entity in your UI. If you have multiple contexts you are just making things more complicated for yourself. If you are using a single context keep in mind that no matter how many times you fetch an object against that context you will get the exact same pointer back.
The NSFetchedResultsController will never create objects, it only fetches and provides them for display. Therefore the NSFetchedResultsController is only reporting the fact that you have created this duplication somewhere else in your code.
Now some questions, do these duplicates get pushed down to disk?
Can you see them in the store file on disk?
When you re-launch your application are the duplicates still there?
When you put a print in your custom -init methods on the NSManagedObject; do they fire more than once?
I have a model some instances of which I need to persist. Only some, not all, since persisting all instances would be wasteful. The model has primaryKey of type Int
I need to be able to pass all objects from background to main thread since Realm objects can only be used by the thread on which they were created. Current version of Realm Swift (0.94) does not seem to support handing the object over to another thread directly.
For persisted objects (the ones saved to storage with write) this is not a problem, I can fetch the object on another thread by primaryKey.
Unpersisted objects, however, are problematic. When I create a new object with the same primaryKey in background (I suppose it should be treated as the same object since it has the same primaryKey) and try to fetch it on the main thread (without persisting changes with write since I don't want in in the storage), I seem to get the old object.
I see the following solutions to this problem:
1) persist all objects (which is undesirable and otherwise unnecessary)
2) keep separate model for the objects I want to persist (leads to code duplication).
3) use init(value: AnyObject) initializer which creates unpersisted object from a Dictionary<String, AnyObject> (would probably require manual copying of object properties to dictionary, tedious and potentially error-prone)
Are there any better solutions?
Offtopic: I haven't tried Realm for Android, is situation any better with it?
You are free to pass unpersisted objects between threads as you wish -- only persisted objects cannot yet be handed over between different threads.
I think your problem is that you are creating two objects that you want to be the same object and there is no way the system can know which one you want.
The solution is as simple as it is generic: create a new object only after checking that its unique attribute does not exist already. This should work equally well with persistent and non persistent objects. Obviously, you need to have a central, thread safe in-memory repository where you can go and create new objects.
You write:
I seem to get the old object.
There should not be any old object if you have checked before.
I am learning a bit on NSCoreData and before introducing it some existing projects I have, I would like to validate my good understanding of the core principles.
From what I have understood, NSCoreData make it easier to manage local storage of object (+retrieval after that) by subclassing our Model class from NSManagedObject rather than from NSObject.
Right ?
I have a few questions then. Let's consider I am building a real estate application with as core model object the class Property that can represent an appartment, a house, and all related information. Currently it is managed in my app as a subclass of NSObject.
1) I retrieve the properties from the server through a search query, and have written a initWithJson : method to populate each instance.
Now if I subclass Property from NSManagedObject, I will create my instances by using
+(id)insertNewObjectForEntityForName:(NSString *)entityName
inManagedObjectContext:(NSManagedObjectContext *)context
and I will be still be able to add a populateWithJson: to my class to fill in the properties.
Then I will create a lot of Property instances in the current managedObjectContext, and if I do a save, they will be stored at the physical layer.
If I call again the same webservice, and retrieve the same JSON content, I will recreate the identical managed objects.
How to avoid redundancy with the [managedObjectContext save:&error] call and not to store physically several time the representation of a single real life property ?
2) Let's say I want to store physically only some properties, for instance only the one the user want to have as favorites.
[managedObjectContext save:&error] will save all created / modified / deleted managed objects from the context to the physical layer, and not only the one I want.
How to achieve that ?
Am I supposed to declare another context (managedObjectContext2), move the instance I want to store in that context, and do the save in that one ?
(I mean, I will have a context just to manipulate the object, create instances from the JSON and represents them in UI ... and a second one to actually do the storage)
Or am I supposed to stores all the objects, and add a isFavorite BOOL property , and then fetching using a predicate on that property ?
3) The app has a common navigation pattern : the UITableView lists Properties instance with the minimum information required, and going on a detail view call a webservice to request more information on a specific Property instance (images, full text description).
Is it a good practice for instance to call the webservice only if the property.fullDescription is nil, and then update the object and store it locally with all detailed information, and the next time only to fetch it locally with a predicate on the property.id ?
What about object that might be updated server-side after they have been created?
Thanks for your lights
1) Retrieve the server data into a temporary form (array of dictionaries?), then for each possible property in the array, check to see if you already have an object in Core Data that matches. If you do, either ignore it or update any changed attributes; if not, create a Property object.
2) Decide which things you want to persist in order to support your app's functions. There's no point in creating a managed object for something you don't want to save. Note, though, that Core Data supports sub-classes if you want both Property and FavoriteProperty.
3) Entirely up to your "business rules"…. How often do you need local data to be updated? The only technical consideration might be the guideline to not keep large files locally that can be re-created on demand.
I have what I think is a simple task.
I have a method called [self getPerson] that makes a simple GET request from a web service for a Person that returns some JSON and then transforms the JSON into an NSManagedObject. checks for an existing identical Person NSManagedObject, and if none is found, saves the Person into core data. No problem.
However, If I fire off this method twice in a row, I get two Person NSMangedObjects persisted into Core Data. For example:
[self getPerson];
[self getPerson]; ---> yields duplicate `Person` objects saved in core data, no good.
How can I ensure that only one Person object is saved in Core Data (no duplicates allowed)?
I know the issue, I just don't know how to fix it. The issue is that I need a transaction. When [self getPerson] fires the first time, the method checks for an already existing identical Person object, finds none, and saves a new Person into core data. This is correct. When I fire [self getPerson] the second time, the method checks for an already existing Person object, doesn't see one, and is then persisting another Person object. This is not correct. I would like to make it so that the second time, and third time, and fourth time, to the 1000th time, checking for an existing Person object will only occur after the managedObjectContext saveoperation is done. Right now the check for an existing object is happening so fast (before the save is done).
Do I need a serial queue? If so, should this be a dispatch_async or dispatch_sync? I've even toyed with the idea of trying to use a performSelectorWithDelay trick.
Once you create the object it will exist in the database regardless of you calling save. So you should not create a managed object if one exists already. It's not entirely clear what your code logic is but from your description you say you transform the JSON to a managed object and then you check for an identical existing one and if none is found you save. Well when you create the managed object you have created it, so it's too late to check if an identical one exists. Saving does not create the object it just saves it to the store if it hasn't already been saved.
So first check if an person object exists with the attributes in the JSON and if not then create a managed object.
Well, in this case a serial queue will ensure that operations are performed in the correct manner.
From you question, maybe I'm missing something, I cannot understand if the getPerson method is responsible to both get and save data. If not, you should do it.
Anyway, if you use JSON and the person you retrieve form the server has a unique identifier, you should use that to query against Core Data and verify if it exists or not. The correct manner to do it is to implement Implementing Find-or-Create Efficiently.
A simple question. Is the any reason for calling the getPerson twice? Could you not prevent it using a flag (or a transient property)? Just simple ideas.