My algorithm is pretty simple. Add an item to a cache. The item is stored in both an NSMutableArray and an NSMutableDictionary. The array serves as a FIFO queue, where if I pass a specified maximum size, I delete the oldest items until the size is lower than the allowed maximum.
When I remove an item, I first remove if from the array, and then from the dictionary. Then I have problems because the item seems to be over-released (so says the debugger, i.e. “does not appear to point to a valid object”).
- (void) addItem:(NSData *)value forKey:(NSString *)key {
ApiResponseCacheItem *item = [[ApiResponseCacheItem alloc] init];
item.cacheKey = key;
item.cacheValue = value;
[queue addObject:item];
[item release];
[dictionary setObject:item forKey:key];
size += [value length];
while (size > kMaxCacheSize && [queue count] > 0) {
ApiResponseCacheItem *oldestItem = [queue objectAtIndex:0];
size -= [oldestItem.cacheValue length];
// remove oldest item
[queue removeObjectAtIndex:0];
[dictionary removeObjectForKey:oldestItem.cacheKey];
}
}
Now, I change the order: first I remove the object from the dictionary, and then from the array, leaving everything else exactly the same. Now all is fine.
while (size > kMaxCacheSize && [queue count] > 0) {
ApiResponseCacheItem *oldestItem = [queue objectAtIndex:0];
size -= [oldestItem.cacheValue length];
[dictionary removeObjectForKey:oldestItem.cacheKey];
// remove oldest item
[queue removeObjectAtIndex:0];
}
Please explain.
So, first of all @WTP is absolutely right, and you should accept his answer. +1 to him.
The right thing to do is not touch ‘item’ after you have released it. And certainly don’t pass it around to other unsuspecting methods after you have already released it.
But, having said that, if you want to understand why the order made a difference as to whether you survived the bug or not, the key is to remember that NSDictionary doesn’t retain its keys, it copies them.
So the crashing sequence was like this:
still +1 (the dictionary made a copy of the object for its own use)
removeObjectForKey:] and down you go.
The lucky sequence where the bug didn’t surface:
the crash
Again, you should do what @WTP advised. This is just background info if you’re curious. Hope that helps.