我正在编写一个自定义文件系统缓存组件,该组件有一个索引字典,表示文件夹中文件的重要属性。
这是一个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]];
}
});
}