在短时间后将多个方法调用分组为一次执行



我正在编写一个自定义文件系统缓存组件,该组件有一个索引字典,表示文件夹中文件的重要属性。

这是一个iOS应用程序,我用Objective-C编写

在实现从缓存中添加/删除对象的不同阶段,需要将索引字典保存到磁盘。

为了防止不必要的操作多次发生,例如,如果对象被添加到缓存中…循环,我想做一个系统,每次修改字典时,都会设置一个状态,以确保在将来的某个时候字典会被保存。但是,这不应该立即发生,以防另一个更改很快发生,在这种情况下,第一个'保存'操作不应该发生,但另一个操作应该排队。

在伪代码:

//当字典被修改并且需要修改时,程序的所有其他部分都会调用这个方法

-(void) dispatchSaveIndexDictionary {
 //cancel any previous requests to save.
//queue up a save operation some short time later.
}

我是如何实现的:

-(void)saveIndexDictionaryDispatchDelayed
{
    NSLog(@"dispatching index save");
    [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(saveIndexDictionaryWriteToDisk) object:nil];
    //Delay 0 means it gets queued up asap, which means the index dictionary on disk remains in sync whenever possible. 
    // - This is to solve the case of adding multiple objects in a for... loop
    [self performSelector:@selector(saveIndexDictionaryWriteToDisk) withObject:nil afterDelay:0];
}
-(void)saveIndexDictionaryWriteToDisk
{
    NSLog(@"Writing cache index to disk : %@", self.cachePath);
    [NSKeyedArchiver archiveRootObject:self.indexDictionary
                                toFile:[OFMFileSystemCache indexDictionaryFullPathWithCachePath:self.cachePath]];
}
使用<<p> em> performSelector: withObject: afterDelay: 0

我期望它总是在任何"调度"操作之后执行"写到磁盘"方法,也就是说,如果任务花费很长时间,我们可以有多个写操作,但"写"操作总是最后发生的事情。

我从日志中看到,这并不总是发生,如果我做一个简单的用例,添加10个文件到缓存中,然后有时我得到'调度索引保存'发生,然后没有调用'写入缓存索引到磁盘'。我真不明白这怎么可能!

是否有一些原因为什么我的实现不是一个好主意(我想必须有,因为它不工作得很好)?对于这种类型的延迟方法调用,您认为什么是良好的安全设计,因为索引与缓存的内容保持最新是至关重要的。也就是说,写入缓存应该总是在所有修改完成后最后进行。

谢谢。

我过去在我的缓存中做过类似的事情。我最终要做的不是使用performSelector:afterDelay:而是建立一个NSTimer。比如:

// elsewhere, setup an NSTimer* ivar called saveTimer
-(void) saveToDisk{
    saveTimer = nil;
    // actually save here
}
-(void)resetSaveTimer{
    if(saveTimer) [saveTimer invalidate];
    saveTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(saveToDisk) userInfo:nil repeats:NO];
}
-(void)doStuffWithCache{
    // do stuff, add stuff, whatever
    [self resetSaveTimer];
}

根据你的线程,你可能还想为每个方法体添加@synchronized(yourCacheDictionary){...},只是为了确保你没有试图写到磁盘,同时也编辑字典等

adam继续。我想把我最终确定的解决方案贴出来。

按照建议使用NSTimer,因为我发现了不一致的行为,我无法用'NSObject performSelector:afterDelay:'方法来解释。

在尝试了计时器方法之后,我需要在一些重要的方面修改解决方案。

1 -确保在一个队列上分配计时器,并为计时器执行正确设置了runloop。我正在使用外部缓存库进行一些缓存操作,这将回调未正确设置的队列。对我来说,最简单的解决方案是始终在主队列上调度计时器调用。

2 -在一个专用的串行队列上调度实际的写操作,这样就不会阻塞主队列(在主队列上调度时,timer方法会触发)。

3 -建议将索引字典写入磁盘方法包装在@synchronized(indexDictionary){}中,以确保内容在写入时不被修改。

#pragma mark - Saving Index Dictionary
-(void)saveIndexDictionaryDispatchDelayed
{
    dispatch_async(dispatch_get_main_queue(), ^{
            if (_dispatchSaveTimer) {
                [_dispatchSaveTimer invalidate];
                _dispatchSaveTimer = nil;
            }
            _dispatchSaveTimer = [NSTimer scheduledTimerWithTimeInterval:0.5
                                                                  target:self
                                                                selector:@selector(saveIndexDictionaryWriteToDisk)
                                                                userInfo:nil
                                                                 repeats:NO];
    });
}
-(void)saveIndexDictionaryWriteToDisk
{
    dispatch_async(_cacheOperationQueue, ^{
        @synchronized (self.indexDictionary) {
            _dispatchSaveTimer = nil;
            NSLog(@"Writing cache index to disk : %@", self.cachePath);
            [NSKeyedArchiver archiveRootObject:self.indexDictionary
                                        toFile:[OFMFileSystemCache indexDictionaryFullPathWithCachePath:self.cachePath]];
        }
    });
}

最新更新