Pointers to existing objects after encodeWithCoder: and initWithCoder: - ios

How does NSCoding deal with encoding and decoding of pointers to other objects rather than values? I have a range of model classes which I need to encode, and they need to reference each other with weak properties. I was surprised to find that the references seemed to be maintained after encoding, saving to a file, then subsequently reading from the file and decoding. This worked even though the memory addresses of the objects which referenced each other changed. I'm interested to know how NSCoding achieves this, and also want to ensure it will work consistently.
In short: if I encode properties containing pointers to other objects, can I rely 100% on the references being maintained after decoding?

tl;dr: Yes, NSKeyedArchiver and NSKeyedUnarchiver maintain the reference relationships of the objects in your object graph; if you encode an object graph where objects reference other objects, you can expect the graph to be decoded in a way that maintains those relationships. You won't necessarily get back the same pointers after encoding and decoding, but the identities of the objects will remain the same.
When you encode an object via -encodeObject:forKey:, NSKeyedArchiver never writes out the pointer address of the object. Instead, it maintains a unique table of objects it's seen before:
If the object you're encoding has never been seen before, it gets added to the table and assigned a UID. It then has -encodeWithCoder: called on it so it can encode all of its properties
If it's been seen before (and has been assigned a UID), nothing happens (but the UID is recorded in its place)
When archiving is complete, the archive contains the unique table of objects, along with another dictionary that represents the structure of the object graph. In place of objects are UIDs.
On decode, when you go to call -decodeObject:forKey:, NSKeyedUnarchiver does the opposite mapping:
It looks at the UID for the key in the archive and searches for the UID in the unique table. If that object has been decoded before, it returns a reference to the decoded object. This is the key part — you won't necessarily get the same pointer on decode that was encoded on encode, but you'll get a reference to the same object instead of a copy
If the UID's never been decoded before, it looks for the encoded object in the unique table corresponding to the UID, looks up the class and calls +alloc, -initWithCoder:, and -awakeAfterUsingCoder on the object. The object is then recorded in the UID-to-object map to be reused if necessary
This is one of the key features of the keyed archiver system, and you can rely on this behavior being maintained.

Related

Saving original order of keys in Dictionary by parsing JSONschema in Swift iOS

In my project I have to parse JSON schema, that comes from server.
It has object "Properties", which in fact like Dictionary in curly braces. And, of course, JSONSerialization.jsonObject parses it as Dictionary.
Everything looks like OK, BUT: I use these Properties for building my view (it defines fields to be fiiled by user). Finally, I have to save order of these fields! But, as we know, immediately after the object is parsed to Dictionary, it looses keys order. Anybody knows how can I parse these object, saving fields order?
Additional information:
Structure of Properties is build by user in WEB, so their count is avsolutely random for mobile client. Furthermore, Every object in properties (e.g. Group) can have its own properties, containing other objects. So we have absolutely random tree of nested objects. And their order is necessary for us.
If you don't care about interoperability, meaning 3rd parties also being able to rely on order, you can try to find a parser that preserves order (such as by reading it into an OrderedMap in Python instead of a regular dict- obviously this will differ by language.)
If you care about 3rd parties, it's trickier. As the last person to respond noted, JSON itself does not support this, and JSON Schema is just JSON as far as parsing goes.

Performance of a primary key lookup in Realm?

I've recently done some benchmarking, and it seems like looking up another object by primary key:
let foo = realm.object(ofType: Bar.self, forPrimaryKey: id)
is more efficient (and in this specific case more readable), than trying to set the property directly as:
class Other: Object {
#objc dynamic var relation: Bar? = nil
let list = List<Bar>()
}
My benchmarking wasn't too thorough though (used only one element in the list, etc.) and I'm wondering if this is actually the case.
Intuition makes me think primary key lookup AND using the relation property above would be O(1) or O(logn). With 1,000,000 records and 1,000,000 lookups:
primary key: ~10s
relation property: ~12s
list property: ~14s
In summary: what is the performance of Realm's object(ofType:forPrimaryKey:) lookup?
Extra credit: when is it beneficial to use LinkingObjects, Lists, etc.? Assuming it's just a readability / convenience wrapper of some sort. In my case it has been more messy / bug prone, so I'm assuming I'm not using Realm in the way it was intended.
Realm isn't a relational database like SQLite. Instead, data is stored in B+ trees. All the data for a given property on a given model type is stored within a single tree, and all data retrieval (whether getting a property value or a linked object) involves traversing such a tree.
Furthermore, when a Realm is opened, the contents of the entire database file are mmaped into memory. When you use one of the Realm SDKs, the objects you create (e.g. Object instances) are actually thin wrappers that store a conceptual pointer to a location in the database file and provide methods to directly read from and write to the object at that location. Likewise, relationships (such as object properties on a model) are references to nodes elsewhere in the tree.
This means that retrieving an object requires the time it takes to traverse the database data structures to locate the required information, plus the time it takes to instantiate an object and initialize it. The latter is effectively a constant-time operation, so we want to look primarily at the former.
As for the situations you've outlined...
If you already know your primary key value, getting an object takes O(log n) time, where n is the number of objects of that particular type in the database. (The time it takes to retrieve a Dog is irrespective of the number of Cats the database contains.)
If you're naively implementing a relational-style foreign key pattern, where you model a link to an object of type U by storing a primary key value (like a string) on some object of type T, it will take O(log t) time to retrieve the primary key value (where t is the number of Ts), and O(log u) time to look up the destination object (as described in the previous bullet point; u = the number of Us).
If you're using an object property on your model type T to model a link to another object, it takes O(log t) time to retrieve the location of the destination object.
Using a list introduces another level of indirection, so retrieving the single object from a one-object list will be slower than retrieving an object directly from an object property.
Object, list, and linking objects properties are not intended to be an alternative to looking up objects via primary keys. Rather, they are intended to model many-to-one, many-to-many, and inverse relationships, respectively. For example, a Cat may have a single Owner, so it makes sense for a Cat model to have a object property pointing to its Owner. A Person may have multiple friends, so it makes sense for a Person model to have a list property containing all their friends (which may contain zero, one, or many other Persons).
Finally, if you're interested in learning more, the entire database stack is open source (except for the sync component, which is a strictly optional peripheral component). You can find the code for the core database engine here. We also have an older article that discusses the high-level design of the database engine; you can find that here.

Mutable array comparison for copied objects

I am trying to do array comparison on 2 mutable arrays. In one of the array, i am storing my model objects and in the other array I am storing a copy of the model objects using [myObject copy]. My model object is a subclass of NSObject so I have added the copyWithZone: method as well. However when I do array compare using isEqualToArray on these two arrays it always returns false. Will the compare not work on copied objects? Or am I going wrong somewhere else?
P.S: As an overview, what I'm trying to do is to check whether something is changed in my model before calling an update service. I want to call the service Only if any of the model objects have changed.
Will the compare not work on copied objects?
You can very easily find out the answer to this question by just copying a single object and checking for equality agains the original.
SPOILER
The results you are going to see will depend on if you have implemented custom hash and isEqual: methods in your class. Without those it will default to the superclasses implementation (NSObject) which considers equality to be the same pointer. Since a copy is a new pointer to the same object, NSObject won't consider them equal.
I would recommend that you read about object equality in this NSHipster article (great to start with) and/or in this article by Mike Ash (if you are feeling curious)
Method isEqualToArray acts as follows. It takes one by one the next objects from two arrays and compare them using isEqual. The latter compares hash (NSInteger property) of NSObjects (or its subclasses).
In general it is the address of the object. hash can be redefined while subclassing but it may cause big problems.
For copied objects you will have different hashes. And thus isEqualToArray is always FALSE.
But if you use simple data classes like NSNumber, NSString as elements to compare, you will get TRUE under copying them.

How to get pointer to an existing NSObject during runtime using its hash value

Assuming I have a hash value of some NSObject during runtime.
Is there a way to find a pointer to that object using just hash value?
I don't want to store pointers to objects and their hashes as keys. I imagine that iOS already doas that.
There is no way, not even an unreliable way, to do this.
Many objects have hashes in ways that makes it impossible to reference it. You will have duplicates because of this. One example, as #Martin said, is NSArrays. NSArrays' hashes are just the number of elements in the array.

Availability of bidictionary structure?

I'm facing a case in my application where I need a bidirectional dictionary data structure, that means a kind of NSDictionary where your can retrieve a key with a value and a value with a key (all values and keys are unique).
Is there such a kind of data structure in C / ObjectiveC ?
You can do it with a NSDictionary:
allKeysForObject: Returns a new array containing the keys
corresponding to all occurrences of a given object in the dictionary.
(NSArray *)allKeysForObject:(id)anObject Parameters anObject The value to look for in the dictionary. Return Value A new array
containing the keys corresponding to all occurrences of anObject in
the dictionary. If no object matching anObject is found, returns an
empty array.
Discussion Each object in the dictionary is sent an isEqual: message
to determine if it’s equal to anObject.
And:
objectForKey: Returns the value associated with a given key.
(id)objectForKey:(id)aKey Parameters aKey The key for which to return the corresponding value. Return Value The value associated with
aKey, or nil if no value is associated with aKey.
Literally, the answer is No.
As a workaround you may create a helper class which manages two dictionaries.
Another approach is to create a thin wrapper around C++ container which implement this: boost's Bimap.
When using ARC and Objective-C objects as values or keys in C++ containers, they will handle NSObjects quite nicely. That is, they take care of memory management as you would expect - and you even get "exception safety" for free. Additionally, C++ standard containers are also a tad faster, use less memory, and provide more options to optimize (e.g. custom allocators).

Resources