If you are calling weak()on one of the immutable types (Number, Float, Long, Double, String), then it returns the object itself. Otherwise it will return a WeakReference instance.
Why is that?
If you are calling weak()on one of the immutable types (Number, Float, Long, Double, String), then it returns the object itself. Otherwise it will return a WeakReference instance.
Preventing circular reference is not the only reason why someone would use WeakReferences:
import Toybox.Lang; import Toybox.WatchUi; import Toybox.System; class ResourceCache { hidden var cache as Dictionary<Symbol, WeakReference> = {} as Dictionary<Symbol, WeakReference>; function initialize() { } function get(symbol as Symbol) as Resource { var cache = self.cache; var value; var weakReference = cache.get(symbol); System.println("weakReference: " + weakReference); // for some reason neither weakReference.stillAlive() nor weakReference.get() works (Symbol Not Found) // if (weakReference != null && weakReference.stillAlive()) { if (cache.hasKey(symbol) && weakReference != null && weakReference.get() != null) { value = weakReference.get() as Resource; logMemory("CACHE: ", ", found symbol: " + symbol.toString()); } else { value = WatchUi.loadResource(symbol); cache.put(symbol, value.weak()); logMemory("CACHE: ", ", loaded symbol: " + symbol.toString() + " => " + value.toString().length() + " bytes"); } return value; } } const CACHE = new ResourceCache();
I think that using WeakReferences for a cache like this would make sense (it would allow an app to use as much memory as possible and use the slow IO less).
The problem is, that now after a lot of time spent on this I realized that the problem is that value.weak() returns a String instead of a WeakReference to a String. So the whole concept doesn't work
You can modify the concept to make it work:
1) Create a container class which holds a Resource and implement a get() accessor method
2) Ensure that you never store / pass the result of get() within your own code. (Obviously at some point you will probably need to pass the result of get() to an API function, but that's ok as long as it's not some kind of memory cache-related function)
class ReferenceContainer { private var reference = null; function initialize(r) { reference = r; } // do not store the return value of get(). // call get() every time you need the value function get() { return reference; } }
Obviously it's not ideal.
I don't think this will work. I think you missed the point. You solved a problem I didn't think of: to be able to "get" to the original object with the same "interface": cache.get(symbol).get(). This is nice, but the problem with this is that my goal wasn't to keep the immutable object in the memory "forever". The whole point of ResourceCache is to keep as many resources as the memory enables in memory, but when there's not enough memory it should be able to free them!
However I went along with your advice so I created this helper function:
hidden static function weakReference(resource as Resource) as WeakReference { var weakReference = resource.weak(); if (weakReference == resource) { weakReference = new ReferenceContainer(resource).weak(); } return weakReference; }
and modified the line:
I think you missed the point.
You like to say this a lot, but in this case it's not true. I understood your intent perfectly.
My idea was to wrap ALL resources in a ResourceContainer, not just primitives. And again, the crucial part is to never store or unnecessarily pass around the result of ResourceContainer.get(). So ResourceCache.get() returns a ResourceContainer, and the calling code has to call ResourceContainer.get() whenever it wants to use the value (by manipulating or inspecting it somehow, or by passing it to an API function).
That way, when the resource container is garbage collected, so are its contents. (The resource container should hold the only "persistent" strong reference to the resource.)
Like I said it's not ideal, and I apologize for not explaining the idea in depth.
This is how I would write ResourceCache.get(), to implement my idea. Of course this assumes that your original idea is viable, which may not be the case (see below)
function get(symbol as Symbol) as ResourceContainer { var cache = self.cache; var container; var weakReference = cache.get(symbol); if (weakReference != null && weakReference.stillAlive()) { container = weakReference.get() as ResourceContainer; } else { var value = WatchUi.loadResource(symbol); container = new ReferenceContainer(value); cache.put(symbol, container.weak()); } return container; }
(*) I do think the one unstated assumption you are making is that an object that is *only* referenced via a weak reference will stay alive as long as there's sufficient memory.
Not sure if this is the case. It could actually be that that as soon as the last strong reference to an object goes out of scope, the object is immediately garbage collected, which means that your cache may not cache anything at all.
https://developer.garmin.com/connect-iq/api-docs/Toybox/Lang/WeakReference.html
A weak reference is a loosely bound reference to another object. If all strong references have been freed, the
WeakReference.get()
method will returnnull
. This allows the developer to avoid circular references.
https://developer.garmin.com/connect-iq/monkey-c/objects-and-memory/
Monkey C is reference counted, which means the runtime system will free memory when the number of objects referencing that memory decrements to zero.
In both of these statements, it seems implicit that memory would be freed immediately, instead of the runtime waiting around for memory to become "full".
i.e. Let's pretend the original code works as intended, in an alternate universe where Object.weak() always returns a WeakReference.
- other code calls ResourceCache.get(:someResource) for the first time
- ResourceCache.get() retrieves the value associated with the key :someResource (let's call it someResourceVal) and stores a weak reference in the cache
- ResourceCache.get() returns someResourceVal, which is stored in a local variable res by the caller
- the caller uses res somehow
- once res goes out of scope in the caller, the number of strong references to someResourceVal will be decremented to 0. At this point it seems logical that someResourceVal should be garbage collected immediately. Why wait?
The wikipedia article on weak references addresses the cache use case:
https://en.wikipedia.org/wiki/Weak_reference
When holding cached data that can be recreated if necessary, weak references allow the cache to be reclaimed, effectively producing discardable memory. This last case (a cache) is distinct from others, as it is preferable that the objects only be garbage collected if necessary, and there is thus a need for finer distinctions within weak references, here a stronger form of a weak reference.
Not to state the obvious, but Monkey C has no such finer distinctions within weak references.
Also not to state the obvious, but Garmin obviously designed its implementation of WeakReference solely to handle the use case of avoiding circular references.