We have choosen to represent objects as arrays. To be able to differentiate objects from normal arrays we have put an object marker as the first slot of the array. The object marker is an EXT item with type 127. The second array slot is kept by the class name. The rest of the array slots are kept by the objects attributes.
We have an example:
@interface MyClass: NSObject <CWPackable>
@property int level;
@property MyClass *link;
- (instancetype) initWithLevel:(int)level link:(MyClass*)link;
@endWhen packed into a MessagePack stream it may look like:
[(127,<00>) "MyClass" 37 NULL]This was the simple example where the link was nil. But look at this code fragment:
MyClass *a = [MyClass.alloc initWithLevel:10 link:nil];
MyClass *b = [MyClass.alloc initWithLevel:20 link:a];
a.link = b;
[myContext packObject:a];To not fall into eternal recursion we need to break the circular reference in some way. We do it by giving the objects labels (set useLabel = YES on pack context). The labels are just consecutive numbers starting at 1. We store the label in the objects markers payload. Let´s start packing:
[(127,<01>) // object a got the label 1, we note that in a table
[(127,<01>) "MyClass" 10 [(127,<02>) "MyClass" 20 Now it is time to insert object a again. But this time we have the object in our table and instead of packing the object we just pack a reference to it. A reference is an array with a single slot that is the object marker. The final result becomes:
[(127,<01>) "MyClass" 10 [(127,<02>) "MyClass" 20 [(127,<01>)]]]The cwpack dump utility "knows" object markers and prints the above message as:
1->MyClass(10 2->MyClass(20 ->1))
It is not just user objects that can contain circular references. Take this example:
NSMutableArray *a = NSMutableArray.new;
[a addObject:a];
[myContext packObject:a];Obviously we need a way to label MessagePack objects also. To that point we divide the MessagePack items in two cathegories: references and values. Arrays and maps are references and all other items are values. For references we do as for user objects, but instead of a class-name in the second slot we put the MessagePack object. In this case, there are just 2 slots. So the packing above becomes:
[(127,<01>) [[(127,<01>)]]]Or prettyprinted: 1->[->1]