Autorelase Pools And Design Thoughts

First of all, let me say hello again, and I hope everyone has had a happy Easter holidays. During that time, I dag ip into the iPhone OS SDK and graphics libraries for it, and I have made a simple game, which I hope to make it more than a simple game in some time. I must say that it proved to be more difficult than I originally thought.

Another problem has risen up, and that is the Launcher OS X 3.0 for FS2_Open. For some reasons, things do not work as they should be. Not as they did back when I built the application. After many months, I decided to lay my hands on the source, once again, only to find out that I really need to rewrite the whole application. I stumbled across points where I said: “What the hell was I thinking when I did THAT?” So, I will need to rewrite the application, and make FS2_Open Launcher 4.0. Which I won’t be able to, not before summer. Until then, I have made a new beta of the Launcher, which furthers improve mod.ini support and changes the way the command line drawer functions to a more comprehensive one.

One of my main problems when building the game, was the deallocation of the sources. When do things get deallocated? One method was to preprocess all data before the level starts, and then reuse those resources until the application quits, which is when the resources will get deallocated. However, that will cause problems, probably, because you can’t preprocess all data on the fly. If you have many data that will be used throughout a level, you must find a way to deallocate this data when you feel that you run out of iPhone resources.

In my case, I have done that using a Cocoa singleton.

Cocoa singletons are Cocoa’s approach to “static global variables”. Which is a good thing. You can thing of singletons as static classes that exist from the start of the application till its termination. Apple explains how to create a singleton in Cocoa. Singletons must be used with care, and caution since overuse can lead to spaghetti code.

static AutoReleasePool *_sharedAutoReleasePool;

@implementation AutoReleasePool
– (id) init{
    self = [super init];
    if (self != nil) {
        NSLog(@”SFAutoreleasePool initializing…”);
        elements = [[NSMutableArray alloc]init];
        maxNumElements = MAX_NUM_ELEMENTS_FOR_RELEASE;
    }
    return self;
}

– (id) initWithMaximumNumberOfElements:(NSInteger)maxElementCount
{
    self = [self init];
    if(self != nil)
        maxNumElements = maxElementCount;
    return self;
}

+ (AutoReleasePool *)sharedAutoreleasePool
{
    @synchronized([AutoReleasePool class])
    {
        if(!_sharedAutoReleasePool)
            [[self alloc]init];
        return _sharedAutoReleasePool;
    }
    // to avoid compiler warning
    return nil;
}

+(id)alloc
{
    @synchronized([AutoReleasePool class])
    {
        NSAssert(_sharedAutoReleasePool == nil, @”Attempted to allocate a second instance of a singleton.”);
        _sharedAutoReleasePool = [super alloc];
        return _sharedAutoReleasePool;
    }
    // to avoid compiler warning
    return nil;
}

– (void)addElementForRelease:(NSObject *)element
{
    if([self elementCount] > self.maxNumElements){
        [self deleteFirstObject];
    }
    [elements addObject:element];
}

– (void)deleteFirstObject
{
    NSLog(@”popping and removing first object…%i objects in pool…, %i objects max”, [elements count], self.maxNumElements);
    NSObject *nodeToRemove = [elements objectAtIndex:0];
    [nodeToRemove performAdditionalDeallocations];
    [elements removeObjectAtIndex:0];
}

– (void) dealloc
{
    [self removeAllObjectsAndRelease];
    [elements release];
    [super dealloc];
}

@end

This was only a concept. I’m sure others will find other better implementations, but this is a general idea how a custom Autorelease Pool inside a program should be setup. Probably the autorelease pool could also be implemented with a good queue implementation concept, but it would give the developer extra headaches with no extra performance gain. Since in Objective C every object is actually a pointer to an allocated block of memory, operations similar to those of a deque or a queue can be very fast.

In the case of the iPhone OS, that autorelease pool may come in handy, since you can’t rely on Cocoa’s autorelease pools to take action, because the iPhone is very limited on resources when compared to any desktop computer. Also, there is no garbage collection on the iPhone, although there are Objective C 2.0 features like fast enumeration and object properties.

You should make sure though, that you don’t overuse Cocoa singletons. Singletons are nice, but they may result in spaghetti code, or compiler issues. If you make many singletons and include them in your project, you may end up including the same file twice. One thing you can do to avoid that is to stay focused to your target and to OOP concepts. For example, each singleton can be #included by any file in your application, but it must not #include classes whose objectsare instantiated inside the application and are part of the GUI or the graphics environment in general. That rule may be broken in certain circumstances, but when working with singletons, ensure that you don’t include a file which includes the one you are calling #include in! Although the #import directive on Cocoa ensures that each file is included only once, in the beforementioned situation, it will surely complain.