我最近遇到了一个问题,我只希望存在一个特定对象的实例,并且只存在执行特定操作所需的短暂时间。它的操作是异步的,因此如果我没有对它进行引用,ARC会在运行循环结束时解除它。如果我确实挂了它,我需要委托回调或通知来知道何时完成释放它。
下载多个图像和其他数据并将其缓存到磁盘所需的对象。因为缓存限制大约是24小时,所以我不希望它在没有缓存项目时浪费内存。我也不需要任何反馈;我希望它能够执行它的任务并完成它自己。
我想出了一个我非常喜欢的设计模式。从那时起我就在其他几个项目中使用它,如果它是一个我不知道的众所周知和分析的模式(自毁单身人士???),那就是好奇。我想知道,所以我可以意识到我目前没有看到任何潜在的陷阱。
我也非常有兴趣听到你们可能有关于为什么这是一个糟糕设计的任何意见。
设计就像这样(这是ARC,但是如果通过类方法释放单例,非弧也可以工作):
一个全局静态对象(实际上不是单例,因为它不是一直存在的)
static MySelfDestructingClass* singleton;
单个公共类方法
+ (void)downloadAndCacheDataIfNeeded
{
//Force synchronized access
@synchronized(singleton){
//We are already doing something, return
if(singleton){
return;
}
NSDate* lastCacheDate = [[NSUserDefaults standardDefaults] objectForKey:kKeyForLastUpdate];
if([[NSDate date] timeIntervalSinceDate:lastCacheDate] > kCacheLimit){
//Our cache is out of date, we need to update
singleton = [[self alloc] init];
[singleton downloadAndCache];
}
}
}
现在我们的实例方法,我们需要我们的对象存活,以便请求可以回来:
- (void)downloadAndCache
{
//This would probably be a delegate, but for simplicity of this example it's a notification
[[NSNotificationCenter defaultCenter] addObserver:self forNotificationWithName:NotificationSomeRequestDidSucceed selector:@selector(someCustomRequestDidSucceed:withData:) object:nil];
[SomeCustomRequest makeRequestWithURL:@"http://www.someURL.com"];
}
- (void)someCustomRequestDidSucceed:(SomeCustomRequest *)request withData:(NSDictionary *)dictionary
{
//Do whatever we need to in order to save our data, or fire off image download requests etc...
....
//Set our lastUpdated time in NSUserDefaults
[[NSUserDefaults standardDefaults] setObject:[NSDate date] forKey:kKeyForLastUpdate];
//Remove our observer
[NSNotificationCenter defaultCenter] removeObserver:self name:NotificationSomeRequestDidSucceed object:nil];
//Release ourselves (ok not really, but tell arc we can be released)
singleton = nil;
}
这样我在应用程序中的任何其他位置都需要做的是:
[MySelfDestructingClass downloadAndCacheDataIfNeeded];
现在这个对象会在需要时下载并在完成后自行释放,或者根本不自行创建。它也不会开始两次下载数据。
我知道这个设计在扩展性和功能性方面存在局限性,但对于像这样的实例,以及我用过的其他实例,我发现它非常有用。
答案 0 :(得分:1)
这很常见的使用块。考虑类似的东西(虽然我可能会以不同的方式处理多个调用......)
void ExecuteWithMySingleSelfDestructingObject(void(^block)(MySelfDestructingClass *object)) {
static MySelfDestructingClass* singleton;
@synchronized(singleton) {
if (singleton) {
// To get past the synchronization primitive, this must be a recursive call.
}
// Whatever other conditions you want to have (like your date check)
singleton = [[MySelfDestructingClass] alloc] init];
@try { block(singleton); }
@finally { singleton = nil; }
}
}
注意双重异常处理(尝试/最后加上@synchronized所做的 - 可能想要更改...
然后用块做任何你想做的事......
ExecuteWithMySingleSelfDestructingObject(^(MySelfDestructingClass *object){
// Do whatever I want with the singleton instance that has
// been given to me as <object>
});
当然,它可能是一种类方法......
+ (void)performBlock:(void(^)(MySelfDestructingClass *object))block {
static MySelfDestructingClass* singleton;
@synchronized(singleton) {
if (singleton) {
// To get past the synchronization primitive, this must be a recursive call.
}
// Whatever other conditions you want to have (like your date check)
singleton = [[self] alloc] init];
@try { block(singleton); }
@finally { singleton = nil; }
}
}
[MySelfDestructingClass performBlock:^(MySelfDestructingClass *object){
// Do whatever I want with the singleton instance that has
// been given to me as <object>
}];
我希望这是有道理的(我可以随意输入,因此语法可能会有所不同,但你应该明白这一点。)