我希望我的班级能够检测到一个新实例与某个现有实例等效(对isEqual
:hash
},并且只创建唯一实例。我认为这里的代码可以完成这项工作,但是我担心它会做一些我无法发现的愚蠢行为......
说它是NSURLRequest子类,如下所示:
// MyClass.h
@interface MyClass : NSMutableURLRequest
@end
// MyClass.m
@implementation MyClass
+ (NSMutableSet *)instances {
static NSMutableSet *_instances;
static dispatch_once_t once;
dispatch_once(&once, ^{ _instances = [[NSMutableSet alloc] init];});
return _instances;
}
- (id)initWithURL:(NSURL *)URL {
self = [super initWithURL:URL];
if (self) {
if ([self.class.instances containsObject:self])
self = [self.class.instances member:self];
else
[self.class.instances addObject:self];
}
return self;
}
// Caller.m
NSURL *urlA = [NSURL urlWithString:@"http://www.yahoo.com"];
MyClass *instance0 = [[MyClass alloc] initWithURL: urlA];
MyClass *instance1 = [[MyClass alloc] initWithURL: urlA]; // 2
BOOL works = instance0 == instance1; // works => YES, but at what hidden cost?
问题:
答案 0 :(得分:5)
这不是疯了,但在手动保留/释放模式下,您需要事先释放self
,否则每次运行此方法时都会泄漏未初始化的对象。在ARC中,将自动为您发布原始实例。
见#1。
答案 1 :(得分:5)
想到一个更好的方法(线下的原始答案),假设你真的想通过URL唯一。如果没有,这也演示了同步原语的使用。
@interface UniqueByURLInstances:NSObject
@property(strong) NSURL *url;
@end
@implementation UniqueByURLInstances
static NSMutableDictionary *InstanceCache()
{
static NSMutableDictionary *cache;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
cache = [NSMutableDictionary new];
});
return cache;
}
static dispatch_queue_t InstanceSerializationQueue()
{
static dispatch_queue_t queue;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
queue = dispatch_queue_create("UniqueByURLInstances queue", DISPATCH_QUEUE_SERIAL);
});
return queue;
}
+ (instancetype)instanceWithURL:(NSURL*)URL
{
__block UniqueByURLInstances *returnValue = nil;
dispatch_sync(InstanceSerializationQueue(), ^{
returnValue = [InstanceCache() objectForKey:URL];
if (!returnValue)
{
returnValue = [[self alloc] initWithURL:URL];
}
});
return returnValue;
}
- (id)initWithURL:(NSURL *)URL
{
__block UniqueByURLInstances* returnValue = self;
dispatch_sync(InstanceSerializationQueue(), ^{
returnValue = [InstanceCache() objectForKey:URL];
if (returnValue) return;
returnValue = [super initWithURL:URL];
if (returnValue) {
[InstanceCache() setObject:returnValue forKey:URL];
}
_url = URL;
});
return returnValue;
}
- (void)dealloc {
dispatch_sync(InstanceSerializationQueue(), ^{
[InstanceCache() removeObjectForKey:_url];
});
// rest o' dealloc dance here
}
@end
警告:上面输入了SO - 从未运行过。我可能搞砸了。假设ARC已启用。是的,当使用工厂方法时,它最终会查找URL两次,但是在分配和初始化的噪声中应该丢失额外的查找。这样做意味着开发人员可以使用工厂或初始化程序,但仍然可以看到唯一的实例但是当该URL的实例已经存在时,将不会在执行工厂方法时进行分配。 / p>
(如果您不能通过URL唯一,那么请返回NSMutableSet并完全跳过工厂方法。)
Chuck说了什么,但还有一些补充说明:
重构您的代码:
+(NSMutableSet*)instances
{
static NSMutableSet *_instances;
dispatch_once( ...., ^{ _instances = [[NSMutableSet alloc] init];});
return instances;
}
然后,只要您想要访问instances
,就可以调用该方法。它将所有代码本地化,并将其与+initialize
隔离开来(这不是什么大不了的事)。
如果您的类可能是从多个线程实例化的,那么您需要使用同步原语包围check-allocate-or-return。我会建议一个dispatch_queue。