Skip to content

Instantly share code, notes, and snippets.

@sodastsai
Created November 23, 2012 12:23
Show Gist options
  • Select an option

  • Save sodastsai/4135395 to your computer and use it in GitHub Desktop.

Select an option

Save sodastsai/4135395 to your computer and use it in GitHub Desktop.
Internal Storage with KVO
#import <Foundation/Foundation.h>
@interface ISStorage : NSObject
+ (ISStorage *)sharedStorage;
- (id)valueForKeyInInternalStorage:(NSString *)key;
- (void)setValue:(id)value forKeyInInternalStorage:(NSString *)key;
@end
#import "ISStorage.h"
@interface ISStorage () {
NSMutableDictionary *_internalStorage;
NSString *_filePath;
}
@property (nonatomic, getter=isDirty) BOOL dirty;
@end
@implementation ISStorage
+ (ISStorage *)sharedStorage {
static dispatch_once_t onceToken;
static ISStorage *sharedStorage = nil;
dispatch_once(&onceToken, ^{
sharedStorage = [[self alloc] init];
});
return sharedStorage;
}
- (id)init {
if (self = [super init]) {
// File Path
NSString *docDirectory = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
_filePath = [docDirectory stringByAppendingPathComponent:@"internal.plist"];
// Create pool
_internalStorage = [NSMutableDictionary dictionaryWithContentsOfFile:_filePath];
if (!_internalStorage) {
// No file. Create new one
_internalStorage = [NSMutableDictionary dictionary];
}
_dirty = NO;
// ************************************************************** //
// KVO
[self addObserver:self forKeyPath:@"dirty" options:NSKeyValueObservingOptionNew context:NULL];
// ************************************************************** //
}
return self;
}
- (void)dealloc {
[self removeObserver:self forKeyPath:@"dirty"];
}
#pragma mark - KVO
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context {
if ([keyPath isEqualToString:@"dirty"]) {
BOOL newDirtyValue = [change[NSKeyValueChangeNewKey] boolValue];
if (newDirtyValue) {
// Update after 5 sec
int64_t delayInSeconds = 5.0f;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
// In background thread
dispatch_queue_t globalBackgroundQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
// Go!
dispatch_after(popTime, globalBackgroundQueue, ^(void){
[_internalStorage writeToFile:_filePath atomically:YES];
_dirty = NO;
});
}
}
}
#pragma mark - Interface
- (id)valueForKeyInInternalStorage:(NSString *)key {
return _internalStorage[key];
}
- (void)setValue:(id)value forKeyInInternalStorage:(NSString *)key {
if (!value) return;
_internalStorage[key] = value;
self.dirty = YES;
}
@end
@sodastsai
Copy link
Author

When using this implementation

  1. If I change the content of internal storage frequently (about 100 times) in 20 seconds
    ===> It only does Disk I/O 4 times.
  2. If I leave my app running in 1 minutes without changing the content of internal storage
    ===> The code for writing disk is never called.
  3. If my app crashes after changing the content of internal storage in "5" seconds
    ===> The change is not saved and lost.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment