We all know about the advantages of immutable objects. Most of the immutable objects that we use are model objects that tend to have a lot or properties. The question is how to initialize those properties without creating a telescoping initializer.

Immutable Objects in Cocoa

First idea that comes to mind is to follow the steps of the platform. Cocoa objects are either mutable or immutable. A good example of such object might be a NSURLRequest which has quite a lot of properties. We use its mutable counterpart NSMutableURLRequest to change the values of those properties. NSMutableURLRequest is a subclass of NSURLRequest which in turn implements NSCopying and NSMutableCopying protocols. We could do the same with our own classes.

Let’s implement a User class and its mutable counterpart:

@interface User : NSObject <NSCopying, NSMutableCopying>

@property (nonnull, nonatomic, readonly) NSString *name;

- (nonnull instancetype)initWithUser:(nonnull User *)user;

@end


@interface MutableUser : User

@property (nullable, nonatomic) NSString *name;

@end
@interface User ()

@property (nonatomic) NSString *name;

@end

@implementation User

- (instancetype)initWithUser:(User *)user {
if (self = [super init]) {
_name = [user.name copy];
}
return self;
}

- (id)copyWithZone:(NSZone *)zone {
return [[User alloc] initWithUser:self];
}

- (id)mutableCopyWithZone:(NSZone *)zone {
return [[MutableUser alloc] initWithUser:self];
}

@end


@implementation MutableUser

@dynamic name;

@end

Now we are able to use those classes the same way we use NSURLRequest. However, there are several problems with this approach:

  • Requires defensive copying to prevent accidental sharing of instances of MutableUser class where immutable User is expected
  • We had to use four lines of code for a single name property
  • We limited our ability to extend class hierarchy

Fortunately there is an alternative way to create immutable objects which is an overlooked builder pattern. It is a very simple pattern which addresses all those problems.

Builder Pattern

Let’s dive straight into implementation but this time we will start with a base class for our model objects - Entity.

@class EntityBuilder;

@interface Entity : NSObject

@property (nonnull, nonatomic, readonly) NSString *ID;

- (nonnull instancetype)initWithBuilder:(nonnull EntityBuilder *)builder;

@end


@interface EntityBuilder : NSObject

@property (nullable, nonatomic) NSString *ID;

- (nonnull Entity *)build;

@end
@implementation Entity

- (instancetype)initWithBuilder:(EntityBuilder *)builder {
// You can also assers that validate builder here
if (self = [super init]) {
_ID = builder.ID;
}
return self;
}

@end


@implementation EntityBuilder

- (Entity *)build {
return [[Entity alloc] initWithBuilder:self];
}

@end

Entity class has no mutable counterpart which means that defensive copying is no longer required. We also used just two lines of code for an ID property.

References