MagicalRecordの中身探訪（セットアップ、NSManagedObjectContext周辺）


コード書いてて色々よくわからなかったのでメモ。

----

# デフォルトのpersistentStoreCoordinator

CoreData初期化時に特に指定がなければSQLiteストアを使用する。

```objc:MagicalRecord+Setup.m
+ (void) setupCoreDataStack
{
    [self setupCoreDataStackWithStoreNamed:[self defaultStoreName]];
}

+ (void) setupCoreDataStackWithStoreNamed:(NSString *)storeName
{
    if ([NSPersistentStoreCoordinator MR_defaultStoreCoordinator] != nil) return;
    
	NSPersistentStoreCoordinator *coordinator = [NSPersistentStoreCoordinator MR_coordinatorWithSqliteStoreNamed:storeName];
    [NSPersistentStoreCoordinator MR_setDefaultStoreCoordinator:coordinator];
	
    [NSManagedObjectContext MR_initializeDefaultContextWithCoordinator:coordinator];
}
```

# デフォルトのSQLiteデータベースファイル名

作成されるファイルはアプリ名（`kCFBundleNameKey`）+ .sqliteとなる。アプリ名が`SampleApp`であれば`SampleApp.sqlite`というファイルになる。

```objc:MagicalRecord.m
+ (NSString *) defaultStoreName;
{
    NSString *defaultName = [[[NSBundle mainBundle] infoDictionary] valueForKey:(id)kCFBundleNameKey];
    if (defaultName == nil)
    {
        defaultName = kMagicalRecordDefaultStoreFileName;
    }
    if (![defaultName hasSuffix:@"sqlite"]) 
    {
        defaultName = [defaultName stringByAppendingPathExtension:@"sqlite"];
    }

    return defaultName;
}
```

# デフォルトのNSManagementObjectContext

`[NSManagedObjectContext MR_defaultContext]`で取得できるコンテキストは、アプリケーション起動後に最初にsetupしたpersistentStoreCoordinator（SQLiteストア）を利用したコンテキストが返される。

```objc:NSManagedObjectContext+MagicalRecord.m
+ (NSManagedObjectContext *) MR_defaultContext
{
	@synchronized (self)
	{
        NSAssert(defaultManagedObjectContext_ != nil, @"Default Context is nil! Did you forget to initialize the Core Data Stack?");
        return defaultManagedObjectContext_;
	}
}
```

iCloudストアを利用した場合は`NSNotificationCenter`のオブザーバ設定が行われる。

```objc:NSManagedObjectContext+MagicalRecord.m
+ (void) MR_setDefaultContext:(NSManagedObjectContext *)moc
{
    if (defaultManagedObjectContext_)
    {
        [[NSNotificationCenter defaultCenter] removeObserver:defaultManagedObjectContext_];
    }
    
    NSPersistentStoreCoordinator *coordinator = [NSPersistentStoreCoordinator MR_defaultStoreCoordinator];
    if (iCloudSetupNotificationObserver)
    {
        [[NSNotificationCenter defaultCenter] removeObserver:iCloudSetupNotificationObserver];
        iCloudSetupNotificationObserver = nil;
    }
    
    if ([MagicalRecord isICloudEnabled]) 
    {
        [defaultManagedObjectContext_ MR_stopObservingiCloudChangesInCoordinator:coordinator];
    }

    defaultManagedObjectContext_ = moc;
    [defaultManagedObjectContext_ MR_setWorkingName:@"DEFAULT"];
    
//    [moc MR_obtainPermanentIDsBeforeSaving];
    if ([MagicalRecord isICloudEnabled])
    {
        [defaultManagedObjectContext_ MR_observeiCloudChangesInCoordinator:coordinator];
    }
    else
    {
        // If icloud is NOT enabled at the time of this method being called, listen for it to be setup later, and THEN set up observing cloud changes
        iCloudSetupNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kMagicalRecordPSCDidCompleteiCloudSetupNotification
                                                                           object:nil
                                                                            queue:[NSOperationQueue mainQueue]
                                                                       usingBlock:^(NSNotification *note) {
                                                                           [[NSManagedObjectContext MR_defaultContext] MR_observeiCloudChangesInCoordinator:coordinator];
                                                                       }];        
    }
    MRLog(@"Set Default Context: %@", defaultManagedObjectContext_);
}
```

# ローカルなコンテキスト

`[NSManagedObjectContext MR_context]`を実行すると、`parentContext`にデフォルトコンテキストが代入されたローカルな`NSManagedObjectContext`オブジェクトを取得することが出来る。

```objc:NSManagedObjectContext+MagicalRecord.m
+ (NSManagedObjectContext *) MR_context;
{
    NSManagedObjectContext *context = [[self alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
    [context setParentContext:[self MR_defaultContext]];
    return context;
}
```

# スレッドコンテキスト

`NSManagedObjectContext MR_contextForCurrentThread]`を使用するとスレッドローカルな`NSManagedObjectContext`を取得することが出来る。上記のローカルなコンテキスト（この呼び方でいいのか？）とは、`MR_contextForCurrentThread`を経由して取得すると、メインスレッドかそれ以外かを考慮したスレッドが返却される点が異なる。

メインスレッドから呼び出した場合は`defualtContext`が、それ以外は上記と同様ローカルなコンテキストが返る。

```objc:NSManagedObjectContext+MagicalThreading.m
+ (NSManagedObjectContext *) MR_contextForCurrentThread;
{
	if ([NSThread isMainThread])
	{
		return [self MR_defaultContext];
	}
	else
	{
		NSMutableDictionary *threadDict = [[NSThread currentThread] threadDictionary];
		NSManagedObjectContext *threadContext = [threadDict objectForKey:kMagicalRecordManagedObjectContextKey];
		if (threadContext == nil)
		{
			threadContext = [self MR_contextWithParent:[NSManagedObjectContext MR_defaultContext]];
			[threadDict setObject:threadContext forKey:kMagicalRecordManagedObjectContextKey];
		}
		return threadContext;
	}
}
```