Objective C中优雅且“正确”的多重实现?

时间:2010-03-24 11:48:08

标签: objective-c design-patterns singleton multiton

你会在objective-c'优雅'中调用这个多重实现吗?我以编程方式“禁止”使用allocallocWithZone:,因为分配或不分配内存的决定需要基于密钥完成。

我确信我只需要处理两个实例,所以我使用'switch-case'代替地图。

#import "Multiton.h"

static Multiton *firstInstance = nil;
static Multiton *secondInstance = nil;

@implementation Multiton

+ (Multiton *) sharedInstanceForDirection:(enum KeyName)direction {

    return [[self allocWithKey:direction] init];
}

+ (id) allocWithKey:(enum KeyName)key {

    return [self allocWithZone:nil andKey:key];
}

+ (id) allocWithZone:(NSZone *)zone andKey:(enum KeyName)key {

    Multiton **sharedInstance;

    @synchronized(self) {

        switch (key) {
            case KEY_1:
                sharedInstance = &firstInstance;
                break;
            case KEY_2:
                sharedInstance = &secondInstance;
                break;
            default:
                [NSException raise:NSInvalidArgumentException format:@"Invalid key"];
                break;
        }
        if (*sharedInstance == nil)
            *sharedInstance = [super allocWithZone:zone];
    }

    return *sharedInstance;
}

+ (id) allocWithZone:(NSZone *)zone {

    //Do not allow use of alloc and allocWithZone
    [NSException raise:NSObjectInaccessibleException format:@"Use allocWithZone:andKey: or allocWithKey:"];
    return nil;
}

- (id) copyWithZone:(NSZone *)zone {

    return self;
}

- (id) retain {

    return self;
}

- (unsigned) retainCount {

    return NSUIntegerMax;
}

- (void) release {

    return;
}

- (id) autorelease {

    return self;
}

- (id) init {
    [super init];
    return self;
}

@end

PS:我还没有尝试过,如果它仍然有效,但它的编译干净利落:)

3 个答案:

答案 0 :(得分:3)

我发现单身人士是一个坏主意,这看起来好像是可怕的四倍。代码非常复杂,你可以肯定花几个小时追逐它中的细微错误,你可能永远不会感到舒服。那不好。你应该把这种可憎的东西扔掉,然后以不需要太多思考的其他方式将你的物体连接在一起。

如果您喜欢图案,可以使用类似于工厂图案的东西来连接对象。工厂将负责创建这两​​个实例并在需要的地方传递它们。工厂将比Multiton简单得多:

@interface Factory : NSObject {
    Foo *foo1, *foo2;
}
@end

@implementation Factory

- (id) init {
    [super init];
    foo1 = [[Foo alloc] init];
    foo2 = [[Foo alloc] init];
    return self;
}

当然,您不必一次创建两个实例。你可以做任何你喜欢的事情 - 缓存,延迟加载,任何东西。关键是将Foo生命周期管理留给工厂,与Foo代码分开。然后它变得容易多了。 ¶所有需要Foo的其他对象都将通过Factory创建并连接,并通过setter接收Foo

@implementation Factory

- (id) wireSomeClass {
    id instance = [[SomeClass alloc] init];
    [instance setFoo:foo1];
    [instance setAnotherDependency:bar];
    return [instance autorelease];
}

这比您问题中的代码简单得多。

答案 1 :(得分:2)

不要覆盖alloc。重写alloc以返回先前分配的类实例的问题是,当+ sharedInstance调用[[Multiton alloc] init]时... + alloc将返回旧实例,然后 -init将重新初始化它!最佳做法是在返回缓存实例之前覆盖-init,执行缓存查找并调用[self release]。

如果您真的关注额外+ alloc的成本(它并不多),您还可以在+ sharedInstance中执行缓存查找,然后确保所有客户端都访问该实例通过+ sharedInstance来避免额外的alloc。

答案 2 :(得分:0)

程序问题:你怎么知道你只有两个实例,或者需要有两个实例? (或者想要有两个实例?)具有“Multiton”的究竟是什么? (那就是一个字?)