2

I am in the process of converting my project to use ARC, and ran into a special problem. I have a class that manages a cache of files that are downloaded from the network. Each file is stored in the iPhone filesystem, and an associated object is kept in my manager class for it. Other objects that want to use a file, request my manager for a cache object, and retain that for as long as they need the file.

But once in a while, the manager cleans up the cache, removing old files. Of course, it should not remove files that are in use at that moment. Before ARC, I detected that by using the retainCount of the associated object:

// if retainCount is 1 then only the cache has a reference to it
if( obj.retainCount <= 1 ) {
    [obj deleteFile];
    [cache removeObject:obj];
}

That worked perfectly [and yes, I know about the warnings about unreliability of retainCount, but in my experience, if retainCount > 1 you know for sure that more than one object has retained it]

However, with ARC, you are not allowed to use retainCount anymore. I could introduce my own retain counting mechanism, and require all objects that use files to explicitly retain and release the file objects. But that is errorprone, and it is exactly the kind of thing that ARC is supposed to solve.

Do you know a better way to achieve the same thing?

vikingosegundo
  • 52,040
  • 14
  • 137
  • 178
fishinear
  • 6,101
  • 3
  • 36
  • 84
  • The only thing, that worth to be said for retainCount: http://stackoverflow.com/a/4636477/106435 – vikingosegundo Mar 02 '12 at 17:30
  • Have you considered [NSCache](https://developer.apple.com/library/ios/#documentation/Cocoa/Reference/NSCache_Class/Reference/Reference.html)? – Caleb Mar 02 '12 at 17:39
  • @Caleb: Rob just gave that answer as well. I did not know about it, and will look into it. – fishinear Mar 02 '12 at 18:06

3 Answers3

5

This feature is best handled by NSCache and NSDiscardableContent. This uses explicit start and end calls which allows you to maintain strong references to things you don't necessarily need kept around (because you'll recreate them automatically). Using NSCache for this also gets you other advantages, such as automatic dumping of discardable content even when you're suspended. Without something like NSCache, you would be killed when memory gets low rather than letting you dump your excess cache.

That said, you've built another system. This particular problem is what weak references are for. Your cache should maintain a weak reference to the objects rather than a strong reference. There are several approaches discussed in Non-retaining array for delegates. I personally prefer the NSValue solution. The approved answer sounds great and simple, but you need to understand ARC and CFArray pretty well in order to use it correctly. The NSValue solution is much less tricky.

Ideally, if your file objects know they are cached, they can tell the cache to remove them during their dealloc. Otherwise, you can periodically clear out empty values from the array.

Community
  • 1
  • 1
Rob Napier
  • 286,113
  • 34
  • 456
  • 610
  • NSCache is a good tip, I did not know about that. I will definitely look into whether I can use that. Weak references I first thought I could not use: it would mean my objects and associated files would get deleted when the are not used anymore. I have a cache exactly to avoid that. But thinking about it: I could let the file objects get cleaned up by weak references, but keep the files. Then the cleanup code could check which references are nil, and delete the associated files. Mmmm, might work. Thanks! – fishinear Mar 02 '12 at 17:53
  • I looked into NSCache, and frankly it does not look very useful. Even though it says that it "incorporates various auto-removal policies", there does not seem to be a way to set the policy to be used. If you know how to set an LRU (or LFU) removal policy, for example, please let me know. A quick internet search did not yield useful answers. – fishinear Mar 02 '12 at 20:30
0

Not sure if this can be applied to your needs, but are you aware of Associative References? These allow you to attached slave objects to a master object, so that the slave object lives as long as the master lives. Not only allows this to attach objects to an object you do not have control over, but you can also learn about their end-of-life.

I wonder if you could use this to monitor your "obj" by attaching a custom class object to it, and that custom class' dealloc would be called if the "obj" gets deallocated. In that custom dealloc you can then perform the file removal.

Thomas Tempelmann
  • 11,045
  • 8
  • 74
  • 149
  • I was not aware of Associative References, and they are pretty impressive, especially together with categories. Thanks for the tip! However, in this case, they are not applicable, because I have control over the original objects. Whatever I do in an associated object, I could just as well do in (the dealloc of) the original object. And I have already implemented explicit reference counting in the original objects, so the problem is not current anymore. – fishinear Nov 13 '12 at 10:55
0

Just set up some int variable in your object's implementation, increment it each time the object is being retained and override its dealloc method to decrement it. The same retain count, but it's only good for custom objects.

Eugene
  • 10,006
  • 4
  • 37
  • 55
  • Well, the dealloc of my file object will not be called, because my cache still has a reference to it. Or do you mean the deallocs of all the different objects that use the file objects? That would be the same as setting up my own reference count system, wouldn't it? – fishinear Mar 02 '12 at 17:30
  • He probably meant to decrement it in release, not dealloc. I'm not sure if this will work though, because I believe ARC bypasses the normal retain and release methods and may not even allow you to override them. – UIAdam Mar 02 '12 at 17:33
  • @UIAdam, under ARC, it is an error to call retain or release at all. – Rob Napier Mar 02 '12 at 17:45
  • @RobNapier Sure, I knew that much is true, but I was slightly uncertain whether ARC will call the "real" retain/release methods for a class on its own or if it uses something different (which I'm pretty sure is the case). – UIAdam Mar 02 '12 at 18:50
  • Yes, it uses a variety of objc_* function calls, and in some cases optimizes even these out. In no case will it call your overridden -retain or -release. – Rob Napier Mar 02 '12 at 18:58