NSManagedObject as MKAnnotation and Core Data Concurrency - ios

I am using what I think is a fairly typical implementation of a NSManagedObject subclass which conforms to MKAnnotation protocol so as to display in a MKMapView. See setters and getters:
-(CLLocationCoordinate2D)coordinate {
CLLocationCoordinate2D coord = EMPTY_LOCATION_COORDINATE;
BOOL validLong = (self.longitude != nil) && ([self.longitude doubleValue] != 0);
BOOL validLat = (self.latitude != nil) && ([self.latitude doubleValue] != 0);
if (validLong && validLat) {
coord.longitude = [self.longitude doubleValue];
coord.latitude = [self.latitude doubleValue];
}
return coord;
}
-(void)setCoordinate:(CLLocationCoordinate2D)coordinate {
if (coordinate.latitude != EMPTY_LOCATION && coordinate.longitude != EMPTY_LOCATION) {
self.latitude = [NSNumber numberWithDouble:coordinate.latitude];
self.longitude = [NSNumber numberWithDouble:coordinate.longitude];
} else {
self.latitude = nil;
self.longitude = nil;
}
}
-(NSString *)title {
NSString *str = [self.projectName copy];
return str;
}
This is working and not causing problems in production at all.
I was debugging some Core Data concurrency issues using Core Data multi-threading assertions and I find that it is flagging the gutter as a concurrency violation. My guess is that the MKMapview that calls for the coordinate is using a background thread and technically that is not allowed. That it works in production is, conceivably, not guaranteed.
I tried to wrap the getter in a [self.managedObjectContext performBlockAndWait^(void){ //set here }]; block but that causes thread locking fail.
Should I ignore the error and move on or is there some better practice for this purpose?

I could not find the reason for this. I verified that the NSManagedObject is on the main queue context. It is being asked for the coordinate on a queue that is not the main queue. What I did to fix is use a proxy object as annotation passed to the MKMapview instead of passing it directly. NSObject class conforming to the MKAnnotation protocol. Initialize with the coordinate and title from my NSManagedObject, pass that instead of the real deal.

First, the coordinate should be implemented as a transient including a magic keyPathsForValuesAffectingCoordinate method. You can start with an uncached transient for simplicity and then perhaps add the additional code to cache it if necessary.
Second, validation should be done using Core Data.
https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/CoreData/ObjectValidation.html#//apple_ref/doc/uid/TP40001075-CH20-SW1

I am not sure that there are not problems with my solution but it was way too complicated for me to make a proxy object. I have tested it and it does not seem to give a thread violation with the -com.apple.CoreData.ConcurrencyDebug option enabled. Since I know my NSManagedObjectContext is on the main queue I did:
- (NSString *) title {
if( [NSThread isMainThread] ) {
return [self.projectName copy];
} else {
__block NSString *title;
NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType: NSPrivateQueueConcurrencyType];
context.persistentStoreCoordinator = self.managedObjectContext.persistentStoreCoordinator;
[context performBlockAndWait: ^{
ActivityLocation *tmp = [context objectWithID: self.objectID];
title = [tmp.projectName copy];
}];
return title;
}
}
I did the same thing for coordinate and subtitle and any other method that was causing problems.
Apparently there is some "wait" operations related to the main queue because if I set the newly created context's parent to 'self's context, it deadlocks.
Interestingly, even though the newly created context is on the thread that is needed, it does not work without the performBlockAndWait: even though if I print the current thread with or without the performBlockAndWait: it prints the same thing.
If there are unsaved changes in the object's projectName property, the old values will be displayed since the persistentStoreCoordinator is used for the newly created context, but I have control over that, unlike my lack of control for which thread Apple calls my MKAnnotation methods on.
I hope this is useful to someone, even though I am responding to a question asked over six years ago!

Related

NSBatchUpdateRequest not saving Transformable attribute

I have an entity (TestEntity) which contains a "Transformable" attribute which holds an object (MyObjectClass). On initial load, the transformable saves correctly; initialised as below:
TestEntity *test = (TestEntity *)[NSEntityDescription insertNewObjectForEntityForName:ENTITY[<Int>] inManagedObjectContext:temporaryContext];
test.transformableAttr = [[MyObjectClass alloc] initWithObject:obj];
However, when I fetch an object (I fetch as dictionary with NSDictionaryResultType) and update its "Transformable" attribute,
MyObjectClass *my_obj = ....
dict[#"transformableAttr"] = my_obj
it saves successfully but when I fetch it again I get nil for the "Transformable" attribute.
Now this only happens with "NSBatchUpdateRequest" because when I save using the MOC
TestEntity *test = ....
test.transformableAttr = updated_object
it saves successfully and I can access the updated attribute when fetched again.
Can anyone please explain? Does it mean that NSBatchUpdateRequest does not Transformable?
My NSBatchUpdateRequest code:
[context performBlock:^{
NSError *requestError = nil;
NSBatchUpdateRequest *batchRequest = [NSBatchUpdateRequest batchUpdateRequestWithEntityName:entity];
batchRequest.resultType = NSUpdatedObjectIDsResultType;
batchRequest.propertiesToUpdate = properties;
NSBatchUpdateResult *result = nil;
SET_IF_NOT_NIL(batchRequest.predicate, predicate)
#try {
result = (NSBatchUpdateResult *)[context executeRequest:batchRequest error:&requestError];
if (requestError != nil){
// #throw
}
if ([result.result respondsToSelector:#selector(count)]){
__block NSInteger counter = [result.result count];
if (counter > 0){
[managedObjectContext performBlock:^{
for(NSManagedObjectID *objectID in result.result){
NSError *faultError = nil;
NSManagedObject *object = [managedObjectContext existingObjectWithID:objectID error:&faultError];
if (object && faultError == nil) {
[managedObjectContext refreshObject:object mergeChanges:YES];
}
counter--;
if (counter <= 0) {
// complete
}
else{
// Wait.
}
}
}];
}
else{
// No Changes
}
}
else {
// No Changes
}
}
#catch (NSException *exception) {
#throw;
}
}];
The documentation doesn't seem to call out this particular scenario, but I'm not surprised that it doesn't work. An NSBatchUpdateRequest is described as [emphasis mine]:
A request to Core Data to do a batch update of data in a persistent store without loading any data into memory.
Transformables work by converting to/from Data in memory. If your class conforms to NSCoding, the coding/decoding happens in memory, because SQLite doesn't know about NSCoding or your classes.
Your original assignment works because Core Data converts the value of transformableAttr to Data in memory and then saves the bytes of the Data to the persistent store. In the batch update, the objects aren't loaded into memory, so the transformation can't run, so the update doesn't work as you'd expect.
It's disappointing that Core Data doesn't make this clearer. Look in the Xcode console to see if it warns you about this. If it doesn't, please file a bug with Apple, because though I don't expect this to work, it's also not good for it to fail silently.
If you want to use batch updates, you'll have to convert your value in code before running the update. I'm not 100% certain how this will work but if your value conforms to NSCoding you'll start with
let transformedData: Data = NSKeyedArchiver.archivedData(withRootObject:transformableAttr)
What you do then is where I'm not sure. You might be able to use transformedData as the new value. Or you might have to access its bytes and use them somehow-- maybe using withUnsafeBytes(_:). You'll probably run into trouble because transformableAttr is not a Data, so it may get messy. It seems that batch updates aren't designed to work well with transformables.

NSManagedObjectContext does not notify observer of changes for transients

I am trying to clean up some transient property recalculation events. In development I have just used a very broad stroke of updating on almost all changes. Now I am trying to determine the best practice, checking for relevant keys and updating as little as needed. My application objects are loaded with dozens of calculations using child and parent attributes where a single change can result in many cascading recalculations.
I thought I understood fairly well, referring to Apple Documentation here and I have experimented with patterns seen on some other StackOverflow posts such as this one by #Marcus S. Zarra. I see Marcus also posted an article on the subject here.
What I have currently is this:
#pragma mark _ worksheetTotal (transient)
+(NSSet *)keyPathsForValuesAffectingWorksheetTotal
{
// local, non-transient, dependent keys here
return [NSSet setWithObjects:#"dailySustenance", nil];
}
-(void)updateWorksheetTotal
{
NSDecimalNumber *lineItemTotal = [self.items valueForKeyPath:#"#sum.lineHoursCost"];
NSDecimalNumber *total = [lineItemTotal decimalNumberByAdding:self.dailySustenance];
[self setWorksheetTotal:total];
}
-(void)setWorksheetTotal:(NSDecimalNumber *)newWorksheetTotal
{
if ([self worksheetTotal] != newWorksheetTotal) {
[self willChangeValueForKey:#"worksheetTotal"];
[self setPrimitiveWorksheetTotal:newWorksheetTotal];
[self didChangeValueForKey:#"worksheetTotal"];
}
}
-(NSDecimalNumber *)worksheetTotal
{
[self willAccessValueForKey:#"worksheetTotal"];
NSDecimalNumber *result = [self primitiveWorksheetTotal];
[self didAccessValueForKey:#"worksheetTotal"];
return result;
}
I am thinking this is straight out of the documentation use case but my observers are not getting notified of the the changes to worksheetTotal.
When a change is made to a lineItem, that notification is received and responds updateWorksheetTotal is called here. I expect this would trigger another notification by the context that worksheetTotal has now changed, but it does not. I have tried numerous variations and combinations of code I have seen but nothing I do seems to make the NSManagedObjectContext consider my update to worksheetTotal to be a change worth reporting to observers.
What am I missing here?
Here is the relevant code for listening for the change in Parent object.
- (void) objectContextDidChange: (NSNotification *) notification
{
NSSet *updatedObjects = [[notification userInfo] objectForKey:NSUpdatedObjectsKey];
// Next look for changes to worksheet that may affect our calculated fields
NSPredicate *worksheetPredicate = [NSPredicate predicateWithFormat:#"entity.name == %# && estimate == %#", #"LaborWorksheet", self];
NSPredicate *materialsPredicate = [NSPredicate predicateWithFormat:#"entity.name == %# && estimate == %#", #"MaterialLineItems", self];
NSPredicate *combinedPredicate = [NSCompoundPredicate orPredicateWithSubpredicates:[NSArray arrayWithObjects:worksheetPredicate, materialsPredicate, nil]];
NSSet *myWorksheets = [updatedObjects filteredSetUsingPredicate:combinedPredicate];
// These are the relevant keys in this estimates labor and materials worksheet
NSSet *relevantKeys = [NSSet setWithObjects:#"worksheetTotal", #"totalEquipmentcost", nil];
BOOL clearCache = NO;
if (myWorksheets.count != 0) {
for (NSManagedObject *worksheet in myWorksheets) {
NSLog(#"got some");
// Here is where it fails, worksheetTotal is not in changedValues.allKeys. It is an empty set.
// Not sure how that could be when I got it from updatedObjects set of userInfo. How could it have no changed values?
NSSet *changedKeys = [NSSet setWithArray:worksheet.changedValues.allKeys];
if ([changedKeys intersectsSet:relevantKeys]) {
clearCache = YES;
NSLog(#"found some, set to clear cache");
// no need to continue checking
break;
}
}
}
if (clearCache) {
// I would update dependent keys here but it is never reached
}
}
The documentation for the changedValues method says:
This method only reports changes to properties that are defined as
persistent properties of the receiver, not changes to transient
properties or custom instance variables. This method does not
unnecessarily fire relationship faults.
Since your property is transient, it will not appear there. I would suggest using KVO to monitor the changes in these properties.
You are returning before didAccessValueForKey. Store to a variable and return it after didAccessValueForKey like
-(NSDecimalNumber *)worksheetTotal
{
[self willAccessValueForKey:#"worksheetTotal"];
NSDecimalNumber *yourVariable = [self primitiveWorksheetTotal];
[self didAccessValueForKey:#"worksheetTotal"];
return yourVariable;
}
I hope it works.
Edit:
In
-(void)setWorksheetTotal:(NSDecimalNumber *)newWorksheetTotal
consider changing
if ([self worksheetTotal] != newWorksheetTotal) {
to
if (worksheetTotal != newWorksheetTotal) {

Core Data reporting old values?

So I'm using MagicalRecord in my iOS application and running into a very bizarre issue. I'm using the following call:
[MagicalRecord saveUsingCurrentThreadContextWithBlockAndWait:^(NSManagedObjectContext *localContext) {
/* look up object in localContext and change property value */ }];
and later on retrieving that new value (both on the main thread) using
[ObjectName MR_findAll].
However, about 1/3 of the time, it is reporting the old value! It is as if Core Data is caching the old version in memory somewhere and I "sometimes" get it and other times don't.
That looks like a forked method not in the primary repo. As such, I can't guarantee that method will even work. Most likely it's a threading issue you're seeing, which is why the contextForCurrentThread methods have been deprecated.
You may have better luck using a method like
[localContext saveToPersistentStoreAndWait:^{ /* called after save */ }];
or
[MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext){ /* make your changes here*/}];
Turns out MagicalRecord caches other MOCs for different threads and those child MOCs were not getting updated even though the parent MOC was saving.
So in
NSManagedObjectContext (MagicalThreading) change this:
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;
To this:
NSMutableDictionary *threadDict = [[NSThread currentThread] threadDictionary];
NSManagedObjectContext *threadContext = [threadDict objectForKey:kMagicalRecordManagedObjectContextKey];
threadContext = [self MR_contextWithParent:[NSManagedObjectContext MR_defaultContext]];
[threadDict setObject:threadContext forKey:kMagicalRecordManagedObjectContextKey];
return threadContext;
Now as to why the child contexts are not getting updated, I have no idea. But once I did that, everything worked again.

Core Data not firing fault for an NSManagedObject instance as property on app delegate

I import the logged in user's data from server into a Core Data Entity called "User". I also keep a reference of this specific User object onto my AppDelegate (as a property) so I can access it elsewhere in my app. The problem I am facing is, when I push another view controller and try to access appdelegate.loggedInUser.id , I see that "id" is nil. Debugger shows this for the object :
$24 = 0x0b28ad30 <User: 0xb28ad30> (entity: User; id: 0xb261160 <x-coredata:///User/tC48E8991-B8A6-4E68-9112-93F9F21DB5382> ; data: <fault>)
My understanding was that the Core Data framework would fire the fault the moment I try to access one of the properties of this object. I am confused as to why me accessing the "id" property of the user is not firing a fault in this case?
EDIT:
This is how create and use the loggedInUser object :
//method to get bgContext
+(NSManagedObjectContext *)getContextOnBgWithParentSetToMainMOC
{
NSManagedObjectContext *tmpContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[tmpContext setParentContext:[Utils getAppDelegate].managedObjectContext];
return tmpContext;
}
//in App Delegate
NSManagedObjectContext *bgContext = [NSManagedObjectContext getContextOnBgWithParentSetToMainMOC];
self.loggedInUser = [User importFromObject:loggedInUserData inContext:bgContext completionBlock:^(NSManagedObjectContext *theContext, NSManagedObject *theManagedObjectWithValuesImported) {}];
//In User.m file
+ (User *)importFromObject:(NSDictionary *)dictionary inContext:(NSManagedObjectContext *)context completionBlock:(TemporaryContextImportReturnBlock)block {
if ( !context ){
context = [NSManagedObjectContext getContextOnBgWithParentSetToMainMOC];
}
NSManagedObjectContext *localContext = context;
User *newUserEntity = [NSEntityDescription insertNewObjectForEntityForName:#"User" inManagedObjectContext:localContext];
NSArray *emailsArray = [dictionary objectForKey:#"emails"];
NSString *emailsString = #"";
if ([emailsArray count] > 0){
emailsString = [emailsArray componentsJoinedByString:#","];
}
newUserEntity.emails = emailsString;
newUserEntity.id = [dictionary objectForKey:#"id"];
newUserEntity.n = [dictionary nonNullObjectForKey:#"n"];
return newUserEntity;
}
//Access in one of the view controllers
User *loggedInUser = [Utils getAppDelegate].loggedInUser;
// loggedInUser.id /*nil*/
I have the same problem. It turns out, according to this answer, which references the Apple docs, that an NSManagedObject does not hold a strong reference to its NSManagedObjectContext as you might expect. I suspect that if you inspect your object when it doesn't fire the fault properly that [myObject managedObjectContext] == nil.
I don't know what best practices are here. The obvious (but potentially difficult) solution is to find out why your MOC is being deallocated. As an alternative, although I'm unsure whether it's safe to do, you could retain the MOC from each NSManagedObject instance. (I have question about that open here.)
make sure, that you do not call
[managedObjectContext reset];
somewhere. From Apple doc:
Returns the receiver to its base state.
All the receiver's managed objects are “forgotten.” If you use this method, you should ensure that you also discard references to any managed objects fetched using the receiver, since they will be invalid afterwards.
Those "orphaned" managed object's managedObjectContext property will change to nil and they will not be able to fire faults anymore.

Making Core Data Thread-safe

Long story short, I'm tired of the absurd concurrency rules associated with NSManagedObjectContext (or rather, its complete lack of support for concurrency and tendency to explode or do other incorrect things if you attempt to share an NSManagedObjectContext across threads), and am trying to implement a thread-safe variant.
Basically what I've done is created a subclass that tracks the thread that it was created on, and then maps all method invocations back to that thread. The mechanism for doing this is slightly convoluted, but the crux of it is that I've got some helper methods like:
- (NSInvocation*) invocationWithSelector:(SEL)selector {
//creates an NSInvocation for the given selector
NSMethodSignature* sig = [self methodSignatureForSelector:selector];
NSInvocation* call = [NSInvocation invocationWithMethodSignature:sig];
[call retainArguments];
call.target = self;
call.selector = selector;
return call;
}
- (void) runInvocationOnContextThread:(NSInvocation*)invocation {
//performs an NSInvocation on the thread associated with this context
NSThread* currentThread = [NSThread currentThread];
if (currentThread != myThread) {
//call over to the correct thread
[self performSelector:#selector(runInvocationOnContextThread:) onThread:myThread withObject:invocation waitUntilDone:YES];
}
else {
//we're okay to invoke the target now
[invocation invoke];
}
}
- (id) runInvocationReturningObject:(NSInvocation*) call {
//returns object types only
[self runInvocationOnContextThread:call];
//now grab the return value
__unsafe_unretained id result = nil;
[call getReturnValue:&result];
return result;
}
...and then the subclass implements the NSManagedContext interface following a pattern like:
- (NSArray*) executeFetchRequest:(NSFetchRequest *)request error:(NSError *__autoreleasing *)error {
//if we're on the context thread, we can directly call the superclass
if ([NSThread currentThread] == myThread) {
return [super executeFetchRequest:request error:error];
}
//if we get here, we need to remap the invocation back to the context thread
#synchronized(self) {
//execute the call on the correct thread for this context
NSInvocation* call = [self invocationWithSelector:#selector(executeFetchRequest:error:) andArg:request];
[call setArgument:&error atIndex:3];
return [self runInvocationReturningObject:call];
}
}
...and then I'm testing it with some code that goes like:
- (void) testContext:(NSManagedObjectContext*) context {
while (true) {
if (arc4random() % 2 == 0) {
//insert
MyEntity* obj = [NSEntityDescription insertNewObjectForEntityForName:#"MyEntity" inManagedObjectContext:context];
obj.someNumber = [NSNumber numberWithDouble:1.0];
obj.anotherNumber = [NSNumber numberWithDouble:1.0];
obj.aString = [NSString stringWithFormat:#"%d", arc4random()];
[context refreshObject:obj mergeChanges:YES];
[context save:nil];
}
else {
//delete
NSArray* others = [context fetchObjectsForEntityName:#"MyEntity"];
if ([others lastObject]) {
MyEntity* target = [others lastObject];
[context deleteObject:target];
[context save:nil];
}
}
[NSThread sleepForTimeInterval:0.1];
}
}
So essentially, I spin up some threads targeting the above entry point, and they randomly create and delete entities. This almost works the way it should.
The problem is that every so often one of the threads will get an EXC_BAD_ACCESS when calling obj.<field> = <value>;. It's not clear to me what the problem is, because if I print obj in the debugger everything looks good. Any suggestions on what the problem might be (other than the fact that Apple recommends against subclassing NSManagedObjectContext) and how to fix it?
P.S. I'm aware of GCD and NSOperationQueue and other techniques typically used to "solve" this problem. None of those offer what I want. What I'm looking for is an NSManagedObjectContext that can be freely, safely, and directly used by any number of threads to view and change application state without requiring any external synchronization.
As noa rightly pointed out, the problem was that although I had made the NSManagedObjectContext thread-safe, I had not instrumented the NSManagedObject instances themselves to be thread-safe. Interactions between the thread-safe context and the non-thread-safe entities were responsible for my periodic crashes.
In case anyone is interested, I created a thread-safe NSManagedObject subclass by injecting my own setter methods in lieu of (some of) the ones that Core Data would normally generate. This is accomplished using code like:
//implement these so that we know what thread our associated context is on
- (void) awakeFromInsert {
myThread = [NSThread currentThread];
}
- (void) awakeFromFetch {
myThread = [NSThread currentThread];
}
//helper for re-invoking the dynamic setter method, because the NSInvocation requires a #selector and dynamicSetter() isn't one
- (void) recallDynamicSetter:(SEL)sel withObject:(id)obj {
dynamicSetter(self, sel, obj);
}
//mapping invocations back to the context thread
- (void) runInvocationOnCorrectThread:(NSInvocation*)call {
if (! [self myThread] || [NSThread currentThread] == [self myThread]) {
//okay to invoke
[call invoke];
}
else {
//remap to the correct thread
[self performSelector:#selector(runInvocationOnCorrectThread:) onThread:myThread withObject:call waitUntilDone:YES];
}
}
//magic! perform the same operations that the Core Data generated setter would, but only after ensuring we are on the correct thread
void dynamicSetter(id self, SEL _cmd, id obj) {
if (! [self myThread] || [NSThread currentThread] == [self myThread]) {
//okay to execute
//XXX: clunky way to get the property name, but meh...
NSString* targetSel = NSStringFromSelector(_cmd);
NSString* propertyNameUpper = [targetSel substringFromIndex:3]; //remove the 'set'
NSString* firstLetter = [[propertyNameUpper substringToIndex:1] lowercaseString];
NSString* propertyName = [NSString stringWithFormat:#"%#%#", firstLetter, [propertyNameUpper substringFromIndex:1]];
propertyName = [propertyName substringToIndex:[propertyName length] - 1];
//NSLog(#"Setting property: name=%#", propertyName);
[self willChangeValueForKey:propertyName];
[self setPrimitiveValue:obj forKey:propertyName];
[self didChangeValueForKey:propertyName];
}
else {
//call back on the correct thread
NSMethodSignature* sig = [self methodSignatureForSelector:#selector(recallDynamicSetter:withObject:)];
NSInvocation* call = [NSInvocation invocationWithMethodSignature:sig];
[call retainArguments];
call.target = self;
call.selector = #selector(recallDynamicSetter:withObject:);
[call setArgument:&_cmd atIndex:2];
[call setArgument:&obj atIndex:3];
[self runInvocationOnCorrectThread:call];
}
}
//bootstrapping the magic; watch for setters and override each one we see
+ (BOOL) resolveInstanceMethod:(SEL)sel {
NSString* targetSel = NSStringFromSelector(sel);
if ([targetSel startsWith:#"set"] && ! [targetSel contains:#"Primitive"]) {
NSLog(#"Overriding selector: %#", targetSel);
class_addMethod([self class], sel, (IMP)dynamicSetter, "v#:#");
return YES;
}
return [super resolveInstanceMethod:sel];
}
This, in conjunction with my thread-safe context implementation, solved the problem and got me what I wanted; a thread-safe context that I can pass around to whomever I want without having to worry about the consequences.
Of course this is not a bulletproof solution, as I have identified at least the following limitations:
/* Also note that using this tool carries several small caveats:
*
* 1. All entities in the data model MUST inherit from 'ThreadSafeManagedObject'. Inheriting directly from
* NSManagedObject is not acceptable and WILL crash the app. Either every entity is thread-safe, or none
* of them are.
*
* 2. You MUST use 'ThreadSafeContext' instead of 'NSManagedObjectContext'. If you don't do this then there
* is no point in using 'ThreadSafeManagedObject' (and vice-versa). You need to use the two classes together,
* or not at all. Note that to "use" ThreadSafeContext, all you have to do is replace every [[NSManagedObjectContext alloc] init]
* with an [[ThreadSafeContext alloc] init].
*
* 3. You SHOULD NOT give any 'ThreadSafeManagedObject' a custom setter implementation. If you implement a custom
* setter, then ThreadSafeManagedObject will not be able to synchronize it, and the data model will no longer
* be thread-safe. Note that it is technically possible to work around this, by replicating the synchronization
* logic on a one-off basis for each custom setter added.
*
* 4. You SHOULD NOT add any additional #dynamic properties to your object, or any additional custom methods named
* like 'set...'. If you do the 'ThreadSafeManagedObject' superclass may attempt to override and synchronize
* your implementation.
*
* 5. If you implement 'awakeFromInsert' or 'awakeFromFetch' in your data model class(es), thne you MUST call
* the superclass implementation of these methods before you do anything else.
*
* 6. You SHOULD NOT directly invoke 'setPrimitiveValue:forKey:' or any variant thereof.
*
*/
However, for most typical small to medium-sized projects I think the benefits of a thread-safe data layer significantly outweigh these limitations.
Why not just instantiate your context using one of the provided concurrency types, and leverage performBlock / performBlockAndWait?
That implements the necessary thread confinement with having to mangle with the implementation of Core Data's accessor methods. Which, as you will soon find out will be either very painful to get right or end quite badly for your users.
A great tutorial by Bart Jacobs entitled: Core Data from Scratch: Concurrency for those that need an elegant solution for iOS 5.0 or later and/or Lion or later. Two approaches are described in detail, the more elegant solution involves parent/child managed object contexts.

Resources