Search code examples
objective-ccocoamodelscocoa-design-patterns

Is this a good (Cocoa-like, Apple-approved) model class?


I've been using Objective-C for a while, but I've not been following Apple's guidelines very well. Recently I read Cocoa Design Patterns and the Model Object Implementation Guide, and I'm trying to do some very simple things, but do them very well.

Have I missed any major concepts? Please don't mention self = [super init]; that's been covered many times on SO already. Feel free to critique my #pragma marks though!

#import "IRTileset.h"
#import "IRTileTemplate.h"

@interface IRTileset () //No longer lists protocols because of Felixyz

@property (retain) NSMutableArray* tileTemplates; //Added because of TechZen

@end

#pragma mark -
@implementation IRTileset

#pragma mark -
#pragma mark Initialization

- (IRTileset*)init
{
    if (![super init])
    {
        return nil;
    }

    tileTemplates = [NSMutableArray new];

    return self;
}

- (void)dealloc
{
    [tileTemplates release];
    [uniqueID release]; //Added because of Felixyz (and because OOPS. Gosh.)
    [super dealloc]; //Moved from beginning to end because of Abizern
}

#pragma mark -
#pragma mark Copying/Archiving

- (IRTileset*)copyWithZone:(NSZone*)zone
{
    IRTileset* copy = [IRTileset new];
    [copy setTileTemplates:tileTemplates]; //No longer insertTileTemplates: because of Peter Hosey
    [copy setUniqueID:uniqueID];

    return copy; //No longer [copy autorelease] because of Jared P
}

- (void)encodeWithCoder:(NSCoder*)encoder
{
    [encoder encodeObject:uniqueID forKey:@"uniqueID"];
    [encoder encodeObject:tileTemplates forKey:@"tileTemplates"];
}

- (IRTileset*)initWithCoder:(NSCoder*)decoder
{
    [self init];

    [self setUniqueID:[decoder decodeObjectForKey:@"uniqueID"]];
    [self setTileTemplates:[decoder decodeObjectForKey:@"tileTemplates"]]; //No longer insertTileTemplates: because of Peter Hosey

    return self;
}

#pragma mark -
#pragma mark Public Accessors

@synthesize uniqueID;
@synthesize tileTemplates;

- (NSUInteger)countOfTileTemplates
{
    return [tileTemplates count];
}

- (void)insertTileTemplates:(NSArray*)someTileTemplates atIndexes:(NSIndexSet*)indexes
{
    [tileTemplates insertObjects:someTileTemplates atIndexes:indexes];
}

- (void)removeTileTemplatesAtIndexes:(NSIndexSet*)indexes
{
    [tileTemplates removeObjectsAtIndexes:indexes];
}

//These are for later.
#pragma mark -
#pragma mark Private Accessors

#pragma mark -
#pragma mark Other

@end

(Edit: I've made the changes suggested so far and commented which answers discuss them, in case anyone needs to know why.)


Solution

  • Please don't mention self = [super init]

    So, why aren't you doing that?

    The same goes for initWithCoder:: You should be using the object returned by [self init], not assuming that it initialized the initial object.

    - (void)dealloc
    {
        [super dealloc];
        [tileTemplates release];
    }
    

    As Abizern said in his comment, [super dealloc] should come last. Otherwise, you're accessing an instance variable of a deallocated object.

    - (IRTileTemplate*)copyWithZone:(NSZone*)zone
    

    The return type here should be id, matching the return type declared by the NSCopying protocol.

    {
        IRTileset* copy = [IRTileset new];
        [copy insertTileTemplates:tileTemplates atIndexes:[NSIndexSet indexSetWithIndex:0]];
        [copy setUniqueID:uniqueID];
    

    You're inserting zero or more objects at one index. Create your index set with a range: location = 0, length = the count of the tileTemplates array. Better yet, just assign to the whole property value:

    copy.tileTemplates = self.tileTemplates;
    

    Or access the instance variables directly:

    copy->tileTemplates = [tileTemplates copy];
    

    (Note that you must do the copy yourself when bypassing property accessors, and that you are copying the array on behalf of the copy.)

        return [copy autorelease];
    }
    

    copyWithZone: should not return an autoreleased object. According to the memory management rules, the caller of copy or copyWithZone: owns the copy, which means it is the caller's job to release it, not copyWithZone:'s.

    @synthesize tileTemplates;
    [et al]
    

    You may want to implement the single-object array accessors as well:

    - (void) insertObjectInTileTemplates:(IRTileTemplate *)template atIndex:(NSUInteger)idx;
    - (void) removeObjectFromTileTemplatesAtIndex:(NSUInteger)idx;
    

    This is optional, of course.