/** ------------------------------------------------------------------------------------- * * Class: Cache * Version: 1.2 * * Author: Justin S. Giboney * * \brief This class is used to store objects in memory so that we don't have as many * calls to the database, and so that we don't duplicate objects. * * This class has an dictionary that holds other objects. The reason for using this rather * than the standard memory is because it allows us to set time out periods, and allows * us constant connections to objects. This constant connection makes it so that the * objects are not collected with the garbage. Another reason for having this class is * so that we do not have to hit the database all the time for information. This allows * us to get the objects from our cache rather than the database. This will save time * and effort. * * To use this class you must have an object and a unique identifier. Using values for * an auto increment from a DBMS will not work. We need to be able to identify each * object with an id. * * * Changelog * 2008 May 01 Version 1.2 I sent this class of to the Cocoa-Dev mailing list and * was told to do the following: * * 1. use NSMutableDictionary instead of an array * 2. use NSTimer instead of a seperate thread * 3. use instance variables instead of static * 4. don't put instance initialization code in +alloc. * It goes in -init. * 5. Naming static variables in all caps is confusing. * All-caps is normally only used for preprocessor macros * 6. This should be changed to @synchronized * // wait for the array to be unlocked * do { * } while (arrayLocked == 1); * 7. Get rid of single line commenting * * 2008 Apr 02 Version 1.1 Updated the class with NSAutoreleasePool actions. So that * it doesn't throw warnings. * * 2008 Feb 22 Version 1.0 Created this Cache Class. Wrote some documentation. * 2008 Feb 25-27 Added all of the methods and functionality. * * * Copyright (c) 2008 Justin S. Giboney * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is furnished * to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * * -----------------------------------------------------------------------------------*/ #import "Cache.h" @implementation Cache /* This is for the singleton method */ static Cache *theInstance = nil; /** This method is the implementation of the singleton design pattern. It will go out and * get the instance of the Cache class. We do not want more than one instance of this * class out at a time. If we did we could have some major issues. */ + (Cache *) getInstance { @synchronized(self) { if (theInstance == nil) { [[self alloc] init]; } } return theInstance; } /** This method is the constructor, and it initializes some of the variables that are needed. */ - (id) init { [super init]; defaultSleepTime = 15 * 60 * 1000000; // 15 minutes x 60 seconds x 1,000,000 microseconds defaultTimeout = 30 * 60; // 30 minutes x 60 seconds theDictionary = [[NSMutableDictionary alloc] init]; // we need a nstimer to run the looping system that cleans the dictionary [NSTimer scheduledTimerWithTimeInterval: defaultSleepTime target: self selector: @selector(run:) userInfo: nil repeats: YES]; return self; } /** This method makes sure that new objects can not be instantiated by calling * the [[alloc] init] method more than once. */ + (id) allocWithZone: (NSZone *) zone { @synchronized(self) { if (theInstance == nil) { theInstance = [super allocWithZone:zone]; return theInstance; } } return nil; } /** This methods checks the dictionay for an object. It requires an id of an object. If the * object is in the array, it will return that object. If it isn't, the method will * return nil. */ - (id) get: (NSString *) theID { NSArray *array = [theDictionary objectForKey: theID]; return [array objectAtIndex: 0]; } /** This method puts the object in an array with a timestamp, then it puts that array into the * dictionary for later retrieval. */ - (void) put: (NSString *) theID with: (id) theObject { NSAutoreleasePool *tempPool = [[NSAutoreleasePool alloc] init]; NSDate *now = [NSDate date]; NSArray *itemArray = [NSArray arrayWithObjects: theObject, now, nil]; // we need to verify that the object isn't already in the dictionary // if it is we just need to touch it id dictionaryObject = [theDictionary objectForKey: theID]; if (dictionaryObject == nil){ [theDictionary setObject: itemArray forKey: theID]; } else { [self touch: theID]; } [tempPool release]; } /** This method touches, or resets the timestamp, of an object. Since the Cache has a timer * any time that we read an object from the Cache, we want to touch it and reset its time * stamp. */ - (void) touch: (NSString *) theID { NSAutoreleasePool *tempPool = [[NSAutoreleasePool alloc] init]; NSDate *now = [NSDate date]; NSDate *date = [[theDictionary objectForKey: theID] objectAtIndex: 1]; date = now; [tempPool release]; } /** This method is our thread that runs every default timer amount. This is what goes * through and removes from the array, objects that are passed the time. */ - (void) run: (NSTimer *) theTimer { NSAutoreleasePool *tempPool = [[NSAutoreleasePool alloc] init]; NSDate *now = [NSDate date]; NSArray *keys = [theDictionary allKeys]; int i, items; items = [keys count]; for (i = 0; i < items; ++i){ NSString *key = [keys objectAtIndex: i]; NSArray *array = [theDictionary objectForKey: key]; NSDate *date = [array objectAtIndex: 1]; if ([date compare: now] == NSOrderedDescending) { [self removeItem: key]; } } [tempPool release]; } /** This method can be called from outside and it removes an item from the dictionary. */ - (void) removeItem: (NSString *) theID { [theDictionary removeObjectForKey: theID]; } /** This method sees if a key is in the array */ - (bool) containsKey: (NSString *) theID { NSArray *array = [theDictionary objectForKey: theID]; if (array == nil){ return false; } else { return true; } } /* The following methods were taken from Apple's website to help with the Singleton * implementation. http://developer.apple.com/documentation/Cocoa/Conceptual/CocoaFundamentals/CocoaObjects/chapter_3_section_10.html * 25 Feb 2008 */ - (id)copyWithZone:(NSZone *)zone { return self; } - (id)retain { return self; } - (unsigned)retainCount { return UINT_MAX; //denotes an object that cannot be released } - (void)release { //do nothing } - (id)autorelease { return self; } @end