lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]

Here's the Lightroom solution. Note that this is working with reference counted Objective-C. Note also that it only matters for Objective-C objects that need to persist their Lua proxy because, for example, they have userdata environments.

We keep a weak table mapping light userdata (the Objective-C object pointer) to full userdata (the Lua proxy for the object). Pushing an existing object from it's Objective-C reference is just a lookup. More clever code would probably use metamethods to do the proxy construction if it didn't already exist. That's all pretty much standard stuff. The weak table means that we will generate at most one proxy for the object in Lua and that that proxy will only stick around as long as there is a Lua reference to the object.

But what if we want it to stick around longer because, for example, the Objective-C code needs to vector through the userdata environment to find other objects?

Well, obviously, we need to create another reference in Lua. So, we maintain a table with a set of strongly referenced objects. An object needs to be in this table if we need to preserve it's Lua proxy and there exist references to it on the Objective-C side of the world. There's our invariant.

How do we know if there are references on the Objective-C side of the world? There will be a reference other than the Lua proxy and hence the retain count will be greater than 1. So, we refine our invariant to, if an object needs to have its Lua proxy preserved and it has a retain count greater than 1, then it needs to be in the strong set.

When we create the proxy, we know there must have been an Objective-C side reference, so we need to add the object to the strong set.

When we read the pointer out of the proxy, the conservative behavior is to do a retain and an autorelease on the pointer and add it to the strong set. This will avoid having the pointer floating loose and potentially having its count get incremented from 1 to 2 where you can't see it happen. You avoid it by doing the increment yourself. Of course, that gets a bit expensive in auto-release pool space. You can reduce that cost by special casing self in your bridging code.

So, now we've got things so that if the retain count goes above 1, we will guarantee membership in the strong set. How do we get things out of the set?

Create a userdata object that exists purely to get collected and have its __gc metamethod called. When the metamethod gets called, scan the strong set looking for objects with retain counts of 1 and remove them from the set. Then create a new object to get collected on the next GC cycle. Provided the number of objects is small, you can do the scan quickly.