원문 : http://swilly.tistory.com/51


iPhone / iPad 내에는 SQLite가 기본적으로 탑재되어 있어, 이를 이용해 관계형 DB를 사용하는 어플을 개발 할 수 있습니다.

하지만 직업 SQLite 관련 API를 사용하지 않고 CoreData라는 ORM환경을 통해 접근하는 경우가 대부분입니다.
CoreData 자체는 굉장히 잘 설계된 훌륭한 물건이지만 멀티쓰레드 환경에서 이를 사용하기 위해서는 몇가지 고려할 사항이 있습니다.
이를 간략하게 정리해보겠습니다.

기본적인 코어데이타의 사용법은 숙지하고 있다고 가정한 상태에서, 멀티스레드 환경에서 어떤걸 고려해야 하는가?

1. NSManagedObjectModel 은 스레드에 안전하다.
2. NSPersistantStoreCoordinator는 스레드에 안전하지 않지만, 사용시 알아서 적절하게 락이 걸리므로 고려할 필요가 없다.
3. NSManagedObjectContext는 스레드에 안전하지 않으므로, 멀티스레드환경을 고려해야한다.

이를위해 개발자는 각 스레드별로 하나의 NSPersistantStoreCoordinator 인스턴스를 공유하는 NSManagedObjectContext 를 별도로 생성해야 합니다. 

일반적으롤 프로젝트 생성시 CoreData를 사용토록 체크하면,  NSPersistantStoreCoordinator와 NSManagedObjectContext를 생성하는 코드가 AppDelegate 내에 생성됩니다.

서브 스레드에서 이를 통해 별도의 NSManagedObjectContext를 아래와 같이 생성해봅니다.

TestAppDelegate* delegate = TestAppDelegate*)[[UIApplication sharedApplication] delegate];

NSPersistentStoreCoordinator* coordinator = [delegate persistentStoreCoordinator];

self.managedObjectContext = [[NSManagedObjectContext allocinit];

[self.managedObjectContext setPersistentStoreCoordinator:coordinator];


위와같이 AppDelegate에 있던 NSPersistentStoreCoordinator 인스턴스를 통해 NSManagedObjectContext를 추가로 생성해줍니다.

이렇게 되면 각각의 NSManagedObjectContext가 NSManagedObject를 따로 관리하게 됩니다.

하지만 이 경우 NSManagedObjectContext에 Save 이벤트가 발생하는 경우 오류를 볼 수 있습니다.
이유인 즉슨, 하나의 NSManagedObject에 대한 참조가 별개의 스레드에서 각각 열려있을 때 한쪽에서 이를 수정했을 경우에 발생합니다.

예를 들어 MainThread 에서 A 라는 NSManagedObject를 참조하고 있는 동안 SubThread에서 A의 내용을 수정하고 Save했을경우 MainThread에 있는 NSManagedObjectContext는 이를 알아차리지 못한다는 겁니다.

이를 위한 해결책으로 각각의 쓰레드에 있는 NSManagedObjectContext에 데이타가 변경되었을 경우 이를 알려줄 NSNotifier를 등록해주게 됩니다.

아래 코드를 보도록 하겠습니다.

[[NSNotificationCenter defaultCenteraddObserver:self

   selector:@selector(contextDidSave:) 

   name:NSManagedObjectContextDidSaveNotification

 object:self.managedObjectContext];


NSNotificationCenter에 기본적인 Observer를 등록하는 코드입니다.
간단히 설명해 보면, self.managedObjectContext에 NSManagedObjectContextDidSaveNotification 이 발생했을경우 @selector(contextDidSave:)를 호출해라. 라는 코드입니다.

NSManagedObjectContextDidSaveNotification의 경우 CoreData에 이미 선언되어 있는 상수값으로서, NSManagedObjectContext에서 save이벤트가 발생할 경우 호출됩니다.

이렇게 Save이벤트가 발생했을때 호출될 contextDidSave 메소드를 작성해 보도록 하겠습니다.

- (void)contextDidSave:(NSNotification *)notification {

TestAppDelegate* delegate = (TestAppDelegate*)[[UIApplication sharedApplication] delegate];

[[delegate managedObjectContext]performSelectorOnMainThread:@selector(mergeChangesFromContextDidSaveNotification:)withObject:notification waitUntilDone:YES];

}


NSNotificationCenter에서 발생하는 이벤트를 받게될 메소드는 항상 NSNotification* 을 인자로 받아야 합니다.
호출된 contextDidSave 메소드는
MainThread의 managedObjectContext의 @selector(mergeChangesFromContextDidSaveNotification:)을 호출하게 됩니다.
위 셀렉터는 NSManagedObjectContext에 대한 변경사항을 동기화 시켜주는 메소드로, 이를 통해 타 쓰레드에서 발생한 NSManagedObjectContext에 대한 변경사항을 통보받아 반영하게 됩니다.

마지막으로 스레드내에서 NSManagedObjectContext의 사용이 끝나면 NSNotification를 해제하여 메모리를 비워줍니다.

[[NSNotificationCenter defaultCenterremoveObserver:selfname:NSManagedObjectContextDidSaveNotification object:self.managedObjectContext];


다됬습니다.

이렇게 처리하는것 만으로 멀티스레드상에서의 코어데이타를 무리없이 사용할 수 있게 됩니다.

위와 같은 방법 외에도, NSPersistantStoreCoordinator의 인스턴스를 각각의 스레드별로 생성하여 사용하는 방법도 있습니다.

이 방법의 경우 좀더 많은 리소스를 요구하게 되므로, 반드시 필요한 경우가 아니라면 가급적 위와같은 방법을 사용하는것이 좋습니다.

Posted by 까칠코더.


티스토리 툴바