Skip to content

Instantly share code, notes, and snippets.

@pookjw
Created June 8, 2025 10:44
Show Gist options
  • Save pookjw/9989aff494ae1ed866cbaf9e60126e21 to your computer and use it in GitHub Desktop.
Save pookjw/9989aff494ae1ed866cbaf9e60126e21 to your computer and use it in GitHub Desktop.
//
// main.m
// MyScript
//
// Created by Jinwoo Kim on 6/6/25.
//
#import <CoreData/CoreData.h>
#include <objc/runtime.h>
#include <objc/message.h>
@interface DataStack : NSObject {
@package NSPersistentStoreCoordinator *_persistentCoordinator;
@package NSManagedObjectContext *_managedObjectContext;
@package NSPersistentHistoryToken *_historyToken;
}
@end
@implementation DataStack
+ (NSManagedObjectModel *)_makeModel {
NSEntityDescription *entity = [[NSEntityDescription alloc] init];
entity.name = @"Entity";
NSAttributeDescription *nameAttribute = [[NSAttributeDescription alloc] init];
nameAttribute.name = @"name";
nameAttribute.attributeType = NSStringAttributeType;
entity.properties = @[nameAttribute];
[nameAttribute release];
NSManagedObjectModel *model = [[NSManagedObjectModel alloc] init];
model.entities = @[entity];
[entity release];
return [model autorelease];
}
+ (NSURL *)_storeURL {
static NSURL *result;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSURL *tmpDirectory = [NSURL fileURLWithPath:NSTemporaryDirectory()];
result = [[[tmpDirectory URLByAppendingPathComponent:[NSUUID UUID].UUIDString isDirectory:NO] URLByAppendingPathExtension:@"sqlite"] retain];
});
return result;
}
- (instancetype)init {
if (self = [super init]) {
NSPersistentStoreCoordinator *persistentCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[DataStack _makeModel]];
NSPersistentStoreDescription *storeDescription = [[NSPersistentStoreDescription alloc] initWithURL:[DataStack _storeURL]];
storeDescription.type = NSSQLiteStoreType;
storeDescription.shouldAddStoreAsynchronously = NO;
[storeDescription setOption:@YES forKey:NSPersistentHistoryTrackingKey];
[storeDescription setOption:@YES forKey:NSPersistentStoreRemoteChangeNotificationPostOptionKey];
[persistentCoordinator addPersistentStoreWithDescription:storeDescription completionHandler:^(NSPersistentStoreDescription * _Nonnull desc, NSError * _Nullable error) {
assert(error == nil);
}];
[storeDescription release];
_persistentCoordinator = persistentCoordinator;
_historyToken = [[persistentCoordinator currentPersistentHistoryTokenFromStores:persistentCoordinator.persistentStores] retain];
NSManagedObjectContext *managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
managedObjectContext.persistentStoreCoordinator = persistentCoordinator;
_managedObjectContext = managedObjectContext;
[NSNotificationCenter.defaultCenter addObserver:self selector:@selector(_didReceiveRemoteChange:) name:NSPersistentStoreRemoteChangeNotification object:persistentCoordinator];
}
return self;
}
- (void)dealloc {
[NSNotificationCenter.defaultCenter removeObserver:self];
[_persistentCoordinator release];
[_managedObjectContext release];
[_historyToken release];
[super dealloc];
}
- (void)_didReceiveRemoteChange:(NSNotification *)notification {
[_persistentCoordinator performBlock:^{
NSPersistentHistoryToken * _Nullable oldHistoryToken = _historyToken;
_historyToken = [notification.userInfo[NSPersistentHistoryTokenKey] retain];
NSPersistentHistoryChangeRequest *request = [NSPersistentHistoryChangeRequest fetchHistoryAfterToken:oldHistoryToken];
[oldHistoryToken release];
request.resultType = NSPersistentHistoryResultTypeTransactionsAndChanges;
[_managedObjectContext performBlock:^{
NSError * _Nullable error = nil;
NSPersistentHistoryResult *result = [_managedObjectContext executeRequest:request error:&error];
assert(result != nil);
NSArray<NSPersistentHistoryTransaction *> *transactions = result.result;
for (NSPersistentHistoryTransaction *transaction in transactions) {
[_managedObjectContext mergeChangesFromContextDidSaveNotification:[transaction objectIDNotification]];
}
}];
}];
}
- (void)_resetPrimaryCache {
[_persistentCoordinator performBlockAndWait:^{
for (NSPersistentStore *store in _persistentCoordinator.persistentStores) {
if (![store isKindOfClass:objc_lookUpClass("NSSQLCore")]) continue;
id _generationalRowCache;
assert(object_getInstanceVariable(store, "_generationalRowCache", (void **)&_generationalRowCache) != NULL);
id oldPrimaryCache;
assert(object_getInstanceVariable(_generationalRowCache, "_primaryCache", (void **)&oldPrimaryCache) != NULL);
id newPrimaryCache = ((id (*)(id, SEL, id))objc_msgSend)([[oldPrimaryCache class] alloc], sel_registerName("initWithPersistentStore:"), store);
[oldPrimaryCache release];
assert(object_setInstanceVariable(_generationalRowCache, "_primaryCache", newPrimaryCache) != NULL);
}
}];
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
DataStack *stack_1 = [[DataStack alloc] init];
DataStack *stack_2 = [[DataStack alloc] init];
__block NSManagedObject *object_1;
[stack_1->_managedObjectContext performBlockAndWait:^{
object_1 = [[NSManagedObject alloc] initWithEntity:stack_1->_persistentCoordinator.managedObjectModel.entitiesByName[@"Entity"] insertIntoManagedObjectContext:stack_1->_managedObjectContext];
NSError * _Nullable error = nil;
assert([stack_1->_managedObjectContext save:&error]);
}];
__block NSManagedObject *object_2;
[stack_2->_managedObjectContext performBlockAndWait:^{
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Entity"];
NSError * _Nullable error = nil;
NSArray<NSManagedObject *> *objects = [stack_2->_managedObjectContext executeFetchRequest:request error:&error];
assert(objects != nil);
assert(objects.count == 1);
object_2 = [objects[0] retain];
}];
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
NSObject *observer = [NSNotificationCenter.defaultCenter addObserverForName:NSManagedObjectContextDidMergeChangesObjectIDsNotification object:stack_2->_managedObjectContext queue:nil usingBlock:^(NSNotification * _Nonnull notification) {
dispatch_semaphore_signal(semaphore);
}];
[stack_1->_managedObjectContext performBlockAndWait:^{
[object_1 setValue:@"Foo" forKey:@"name"];
NSError * _Nullable error = nil;
assert([stack_1->_managedObjectContext save:&error]);
}];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
[NSNotificationCenter.defaultCenter removeObserver:observer];
#define MODE 1
#if MODE == 0
[stack_2->_managedObjectContext performBlockAndWait:^{
NSError * _Nullable error = nil;
[stack_2->_managedObjectContext setQueryGenerationFromToken:[NSQueryGenerationToken currentQueryGenerationToken] error:&error];
assert(error == nil);
NSString *name = [object_2 valueForKey:@"name"];
assert([name isEqualToString:@"Foo"]);
}];
#elif MODE == 1
[stack_2->_managedObjectContext performBlockAndWait:^{
[stack_2 _resetPrimaryCache];
NSString *name = [object_2 valueForKey:@"name"];
assert([name isEqualToString:@"Foo"]);
}];
#elif MODE == 2
[stack_2->_managedObjectContext performBlockAndWait:^{
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Entity"];
NSError * _Nullable error = nil;
NSManagedObject *object_2_1 = [stack_2->_managedObjectContext executeFetchRequest:fetchRequest error:&error].firstObject;
assert(error == nil);
assert(object_2_1 != nil);
NSString *name = [object_2_1 valueForKey:@"name"];
assert([name isEqualToString:@"Foo"]);
}];
#elif MODE == 3
[stack_2->_managedObjectContext performBlockAndWait:^{
[object_2 valueForKey:@"name"];
[stack_2->_managedObjectContext refreshObject:object_2 mergeChanges:YES];
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Entity"];
NSError * _Nullable error = nil;
NSManagedObject *object_2_1 = [stack_2->_managedObjectContext executeFetchRequest:fetchRequest error:&error].firstObject;
assert(error == nil);
assert(object_2_1 != nil);
NSString *name = [object_2_1 valueForKey:@"name"];
assert([name isEqualToString:@"Foo"]);
}];
#endif
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment