优雅且“正确”; Objective C 中的多实例实现?
你会称 Objective-C 中 multiton 的这种实现“优雅”吗?我以编程方式“禁止”使用 alloc
和 allocWithZone:
因为分配或不分配内存的决定需要基于键来完成。
我确信我只需要使用两个实例,因此我使用“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:我还没有尝试过这是否有效,但它的编译干净:)
Would you call this implementation of a multiton in objective-c 'elegant'? I have programmatically 'disallowed' use of alloc
and allocWithZone:
because the decision to allocate or not allocate memory needs to be done based on a key.
I know for sure that I need to work with only two instances, so I'm using 'switch-case' instead of a map.
#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: I've not tried out if this works as yet, but its compiling cleanly :)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
我发现单身是一个坏主意,这看起来可怕四倍。该代码非常复杂,您肯定会花费几个小时来追踪其中的细微错误,并且您可能永远不会对此感到舒服。那可不好。您应该扔掉这种令人厌恶的东西,并以其他不需要太多思考的方式将您的对象连接在一起。
如果您喜欢模式,您可以使用类似于工厂模式的东西来连接您的对象。工厂将负责创建这两个实例并将它们传递到需要的地方。并且 Factory 将比 Multiton 简单得多:
当然,您不必同时创建两个实例。你可以在那里做任何你喜欢的事情——缓存、延迟加载,任何事情。重点是将
Foo
生命周期管理留给工厂,与Foo
代码分开。然后事情就变得容易多了。 ¶ 所有其他需要Foo
的对象都将通过 Factory 创建和连接,并通过 setter 接收它们的Foo
:这比您问题中的代码要简单得多。
I find singletons a bad idea and this looks about four times as horrible. The code is quite complex, you can be sure of spending a nice few hours chasing subtle bugs in it and you will probably never feel comfortable about it. That’s no good. You should throw this abomination away and wire your objects together in some other way that doesn’t require so much thinking.
If you like patterns, you can use something akin to Factory pattern to wire your objects. The Factory will take care of creating those two instances and passing them wherever needed. And the Factory will be a lot more simple than Multiton:
Of course you don’t have to create both instances at once. You can do anything you like there – cache, lazy load, anything. The point is leaving the
Foo
lifetime management up to the Factory, separate from theFoo
code. Then it gets much easier. ¶ All the other objects that needFoo
will be created and wired through Factory and will receive theirFoo
through a setter:This is all much more straightforward then the code from your question.
不要覆盖分配。正如您所做的那样,重写 alloc 以返回先前分配的类实例的问题是,当 +sharedInstance 调用 [[Multiton alloc] init]... +alloc 将返回旧实例时,然后 -init 将返回重新初始化它! 最佳实践是覆盖 -init,在返回缓存实例之前执行缓存查找并调用 [self release]。
如果您确实担心额外的 +alloc 的成本(并不多),您还可以在 +sharedInstance 中进行缓存查找,然后确保所有客户端都通过 +sharedInstance 访问该实例以避免额外的分配。
Don't override alloc. The problem with overriding alloc to return a previously allocated instance of the class, as you do, is that when +sharedInstance calls [[Multiton alloc] init]... +alloc will return the old instance, then -init will re-initialize it! The best practice is to override -init, doing the cache lookup and calling [self release] before you return the cached instance.
If you're really concerned about the cost of that extra +alloc (it's not much), you also do your cache lookup in +sharedInstance and then ensure that all of your clients access the instance through +sharedInstance to avoid the extra alloc.
程序要点:您如何知道您只会有两个实例,或者需要有两个实例? (或者想要有两个实例?)拥有“Multiton”的点到底是什么? (这还是一个词吗?)
Point of order: How do you know that you'll only ever have two instances, or need to have two instances? (Or want to have two instances?) What, exactly, is the point of having a "Multiton"? (And is that even a word?)