PDA

View Full Version : Caching objects and multithreading




idelovski
Mar 4, 2012, 06:51 PM
When I started creating my caching class I was sure I'll use it always on the main thread but it turns out I need it in multiple threads because UIScrollView in tiled mode draws the tiles in background threads. So now I need my cache to be thread safe.

I had to solve two problems: One, mutable array/dictionary holding cached images, and two, returned objects could dissapear while being used in another thread.

Am I doing this right?

- (UIImage *)cachedImageForAddress:(NSString *)imgAddress
{
UIImage *retImage = nil;

@synchronized(self) {
retImage = [self.imageDictionary objectForKey:imgAddress];
}

return ([[retImage retain] autorelease]);
}

- (BOOL)cacheImage:(UIImage *)newImage forAddress:(NSString *)imgAddress
{
BOOL retVal = YES;

@synchronized(self) {

if (![self.imgDicKeys containsObject:imgAddress]) {
[self.imageDictionary setObject:newImage forKey:imgAddress];
[self.imgDicKeys addObject:imgAddress];

// See if thre's too many

[self trimImageDictionary:self.imageDictionary
withKeys:self.imgDicKeys
toMaxSize:kMaxImagesInCache];
}
else if (![self.imageDictionary objectForKey:imgAddress])
NSLog (@"Inconsistant image cache!");
else
retVal = NO;
}

return (retVal);
}



PhoneyDeveloper
Mar 4, 2012, 07:34 PM
There's a race condition in your getter. The returned value could be invalidated on another thread between the time you get it from the dictionary and when it's retained. I think you need to retain it inside the @synchronized block.

I don't understand what the keys array is for.

idelovski
Mar 4, 2012, 08:53 PM
There's a race condition in your getter. The returned value could be invalidated on another thread between the time you get it from the dictionary and when it's retained. I think you need to retain it inside the @synchronized block.

Oh, thanks for the tip. Now it looks obvious. ;)

I don't understand what the keys array is for.

I wasn't sure if allKeys property of the dictionary would always have the oldest element at zero index so I created my own array. Plus, in few methods I compare counts of objects in dictionary and array just to make sure there wasn't some sort of a mixup.