SkypeKit Object Lifecycle

SkypeKit objects are reference-counted and garbage collected. The garbage collection check is triggered on object dereferencing (object's finalize method). If the reference count of an object reaches zero, the object will be destructed immediately upon exiting the last scope it was used in.

To guarantee that an object is kept in memory, at least one xxxRef instance referencing that object must be kept alive. However, object properties are maintained separately from the objects themselves, so this will only guarantee that the object itself is not subject to garbage collection. In addition, there is no guarantee that object properties will remain in the property cache throughout object's entire lifecycle. Therefore, you should not keep direct pointers to cached objects or their properties - unless a non-finalized xxxRef to that object exists, such pointers may become invalid at unpredictable times.

The garbage collection method - Skype::cleanup() - is directly called from Skype class destructor and account login. The latter becomes important when a client UI supports logging out and then logging back in (even with the same account as before). The objects and properties caches are always fully flushed and a timestamp is then incremented so that reference from previous logging are invalidated.

Saying this again, for emphasis: after programmatic logout, both object and property caches will be fully flushed. All references to all objects (except your Skype object), will become invalid and can no longer be used.

It is also possible to initiate garbage collection manually. For this you will need to call Skype::cleanup() with argument set to 0. As the cleanup method is in the protected section of the class, you will need to write your own wrapper method around the cleanup function.

class MySkype : public Skype {
public:
  void ManualGarbageCollection() { cleanup(0); }
};

Skype::cleanup() has two arguments:


Object Lifecycle, Part 2: The way of set_dispatch_all()

Now, you may end up with a use case where there is very little sense keeping live references to large number of objects, just in case once in a blue moon, some of those may receive property updates.

A good example of this would be the Message class. Keeping live references for all the messages would quickly end up with almost all of your wrapper cache consisting of Message objects. Even with heavy-traffic accounts, Contacts and Conversations number in hundreds and this number does not grow very fast. The number of Messages, however, is likely to grow much faster. Even with averaging just 100 chat messages per day, an account would end up with 182500 messages in just 5 years.

Consider now, that you may want your UI to respond to message edits (remote user changes the message text after posting it). There is no special event for this. The only way to detect a Message edit is to monitor Message::P_EDIT_TIMESTAMP property changes. To get these property changes you would need to keep a large number of message in cache, just for this purpose.

As of SDK version 3.3 there is a way to somewhat relax the object lifecycle rules - the Skype::set_dispatch_all method.

skype = new MySkype();
skype->set_dispatch_all();

Using this will cause all incoming property updates and events to (briefly) construct the corresponding object if it did not exist previously in the wrapper object cache.

NB! This ONLY works when there is a property update or an event. This does NOT work when an object is just created. This does not, for example, cause Message object to be created when an incoming chat message occurs. In case of an incoming message - the Message object already has its properties at the time of construction - transition from nonexistance to existance is not conidered an update. So, to catch arrival of Message objects you would still need to use Skype::OnMessage callback.

Another important point is that object creation through set_dispatch_all only creates affected objects for the duration of OnChange or event callback. They will not remain in the object cache. After exiting the callback scope, the objects will still be immediately garbage collected from the cache.

In case of multiple sequential property updates to the same object, the object will be constructed and destructed for each update.

To illustrate this, lets play this through with Message edit. First, lets have a MyMessage class with following debug outputs:

MyMessage::MyMessage(unsigned int oid, SERootObject* root) : Message(oid, root) 
{
    printf("MyMessage constructed.\n");
};

MyMessage::~MyMessage()
{
    printf("MyMessage destructed.\n");
};

void MyMessage::OnChange(int prop)
{
    SEString propAsString;
    SEStringList dbg;
    propAsString = this->GetProp(prop);
    dbg = this->getPropDebug(prop, propAsString);
    printf("MyMessage OnChange fired: %s %s\n", (const char*)dbg[1], (const char*)dbg[2]);
};

In case of a new incoming chat message - none of these will fire. The Message object will appear in local runtime already fully up-to-date. To catch a new message, you would still need to override Skype::OnMessage or Conversation::OnChange events.

In case of remote author of the message using edit, you will receive a sequence of three property updates, as follows:

Now accepting incoming chat messages.
Press ENTER to quit.
MyMessage constructed.
MyMessage OnChange fired: EDIT_TIMESTAMP 1302255937
MyMessage destructed.
MyMessage constructed.
MyMessage OnChange fired: EDITED_BY anappo
MyMessage destructed.
MyMessage constructed.
MyMessage OnChange fired: BODY_XML edited
MyMessage destructed.

As you can see, using set_dispatch_all() does have a drawback of additional object creation/destruction overhead. On the other hand, doing object lookups from a huge cache has overhead as well. Also, not having to maintain the reference tree for everything will result in somewhat simpler UI code.


 All Classes Files Functions Variables Typedefs Enumerations Enumerator Friends Defines

(c) Skype Technologies S.A. Confidential/Proprietary

Last updated: Fri Mar 16 2012