4

我最近遇到了一个问题,我只希望特定对象的一个​​实例存在,并且只存在执行特定操作所需的短暂时间。它的操作是异步的,所以如果我没有对它的引用,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];

现在这个对象会在需要时下载东西并在完成后释放自己,或者根本不创建自己。它也不会开始下载数据两次。

我知道这种设计在可扩展性和功能方面存在局限性,但是对于像这样的实例以及我使用过的其他实例,我发现它非常有用。

4

1 回答 1

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>
}];

我希望这是有道理的(我是徒手输入的,所以语法可能会有所不同,但你应该明白这一点)。

于 2012-05-18T01:42:45.823 回答