0

I am creating a NSManagedObject (subclass) with certain attributes. At the same time, I am executing some code/a block that does some network operation given the attributes of my NSManagedObject. Now, some times that network operation might fail or take too long, so I want to add the ability to cancel the execution of that code/block.

I was thinking of making the code/block an NSThread, and then I have the ability to call [theThread cancel]. However, how do I associate the NSThread with my NSManagedObject, given that I cannot add properties to NSManagedObject Categories? Is it OK to just add the property to the definition of the NSManagedObject itself? Seems legal, but subsequent changes to the Core Data model would overwrite my code, I guess.

But maybe there is an entirely different and better way to accomplish what I am trying to do? Any ideas?

user1013725
  • 571
  • 1
  • 4
  • 17

1 Answers1

2

First, new code really should prefer GCD or NSOperationQueue over NSThread. If you find yourself using NSThread it's time to slow down and revisit your design and implementation requirements.

Second, using NSManagedObject across threads is really, really bad. If you do anything but exceedingly trivial things, it can get very difficult to do right as well.

Finally, no matter how you do your threaded network access, you should prefer to grab the data from the managed object, and pass that instead of the managed object itself

If you must access the managed object, make sure your managed object context is of either NSMainQueueConcurrencyType or NSPrivateQueueConcurrencyType and access the managed object like by invoking performBlock or performBlockAndWait using the managedObjectContext property of the managed object.

EDIT

Ok, let me check this with you. What I a currently doing is spawning a backgroundContext, create a new NSManagedObject using performBlock, then save that background Context, switch to the parent context (using performblock), obtain the newly created object in that context using existingObjectWithId:. Then, I create a NSOperation subclass, tie the NSManagedObject (from the parent context) to that NSOperation subclass (it's a property on the subclass) and put that operation in a NSOperationQueue. Within that NSOperation, the NSManagedObject gets changed. It seems to work fine, does that look ok? – user1013725

Um... maybe??? I didn't follow that. Could you please post the code? That would be much more precise and more easy to understand.

@JodyHagins So I am not using performBlock, but maybe that's ok because the managedObjectContext is the main context? – user1013725

No.

If the main context is created with either alloc] init] or alloc] initWithConcurrencyType:NSConfinementConcurrencyType then you must use it only when you know you are running on the main thread.

If it is created with alloc] initWithConcurrencyType:NSMainThreadConcurrencyType then you must use it only when you know you are running on the main thread or within one of the performBlock methods.

Jody Hagins
  • 27,943
  • 6
  • 58
  • 87
  • Thanks, I am using NSOperation now. Also, the NSOperation subclass has a reference to a NSManagedObject whose context is the main managedObjectContext. I think it works fine for now, I haven't seen any problems. I guess my actual question is how to do associate certain properties with NSManagedObject subclasses. They are not allowed in categories, so I can just add them as properties to the subclass. However, if I regenerate the subclass automatically in the CoreDataModel, my changes would get overwritten, so I am wondering what the best way to do so is. – user1013725 Sep 12 '12 at 18:43
  • 2
    take note that if you spawn an NSOperation in a background thread, you have to create a NEW NSManagedObjectContext from within that thread, using the main context's NSPersistantStoreCoordinator, if you plan to save, modify any entities – jere Sep 12 '12 at 18:47
  • 2
    To reiterate @jere's point: you should never be thinking about tying an NSManagedObject to a thread. You should *always* be thinking about tying an NSManagedObjectContext to a thread. Each thread should have its own context (as Jody points out, using queues changes that slightly, but the principle is the same that the unit is the context, not the object). – Rob Napier Sep 12 '12 at 18:49
  • @user1013725 Any information you want from your managed object should already be in the model. If the data is not already accessible to the managed object, you certainly don't want to use it. – Jody Hagins Sep 12 '12 at 18:51
  • @jere - Yes... unless the main context is already allocated as `NSMainQueueConcurrencyType` and then it can be accessed via `performBlock` and `performBlockAndWait`. However, both you and @RobNapier are spot on about using managed objects across threads, and it just underscores the point that it is at least an intermediate level API. – Jody Hagins Sep 12 '12 at 18:58
  • so, you mean I could perform save operations from a background thread using the main context using `NSMainQueueConcurrencyType` and `performBlock` or `performBlockAndWait`, without creating a separate `NSManagedObjectContext`? – jere Sep 12 '12 at 19:03
  • @jere Yes. That's what they are there for. Basically, when you call one of the `performBlock` methods, you send it a block of code to execute. It will then make sure that all the necessary Core Data magic has been performed before and after calling the code in your block. `performBlock` will happen asynchronously, `performBlockAndWait` will cause the calling thread to wait until the operation has completed, but in both cases the appropriate synchronization will take place prior to executing your code block. – Jody Hagins Sep 12 '12 at 19:08
  • Ok, let me check this with you. What I a currently doing is spawning a backgroundContext, create a new NSManagedObject using performBlock, then save that background Context, switch to the parent context (using performblock), obtain the newly created object in that context using existingObjectWithId:. Then, I create a NSOperation subclass, tie the NSManagedObject (from the parent context) to that NSOperation subclass (it's a property on the subclass) and put that operation in a NSOperationQueue. Within that NSOperation, the NSManagedObject gets changed. It seems to work fine, does that look ok? – user1013725 Sep 12 '12 at 19:26
  • @JodyHagins So I am not using performBlock, but maybe that's ok because the managedObjectContext is the main context? – user1013725 Sep 12 '12 at 19:33