1

I'm using AFNetworking with custom NSOperations and Core data, the Core Data concurrency is well managed and the NSOperation uses a proper Managed object context, something like: (is an abstraction)

// MO created in context A
NSManagedObject * foo = ...;
__Block NSManagedObjectID * fooId = foo.objectID;

NMBlockOperation * customOp = [[NMBlockOperation alloc] initWithBlock:^(NMOperation *operation) {

// MO created in context B   
NSManagedObject * safeFoo = [[operation.coreDataManager mocForCurrentThread] objectWithID: fooId];         
...

[_networkManager POST:[self apiUrlStringForPath: xxx]
                   parameters:parameters
                      success:^(AFHTTPRequestOperation *operation, id responseObject) {

                      //Do something with the foo object

                      // MO created in context C   
                      NSManagedObject * safeFoo = [[operation.coreDataManager mocForCurrentThread] objectWithID: fooId];     
[operation finish];
                      } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
    [operation fail];
                      }];        
        }];

... add customOp to a queue

I'm forced to pass the Managed object from context to context due the fact that the context A, B and C are created in different threads, Is possible force the AFNetworking call to execute the failure/success blocks in the caller's thread? Using this approach I could be able to pass the Managed objects only from context A to the NSOperation's context B.

I know how to set the network manager completion queue but this doesn't help me, I want the same caller's thread.

//configure standard network manager
_networkManager = [[AFHTTPRequestOperationManager alloc] init];
//Configure completation queue
dispatch_queue_t backgroundQueue = dispatch_queue_create("com.xxx.afnetComplQueue", NULL);
_networkManager.completionQueue = backgroundQueue;
Kappe
  • 9,217
  • 2
  • 29
  • 41
  • 1
    How would you guarantie that requested thread still exists (as well as confinement moc) at the moment completion blocks are to be executed? This could only work well if you really maintain some single worker thread and schedule all the work directly there. Guess you're not, bet, you're using NSOperationQueue or gcd queues. This could go even worse, if you've managed to use the same single thread again, the same thread gets reused and new local moc spawned. – MANIAK_dobrii May 13 '15 at 13:55
  • Sorry MANIAK_dobrii I've missed some details for simplicity, yes i'm using an NSOperationQueue, and the queue have its own moc (created only once), yes you are right, the thread may no longer exist. – Kappe May 13 '15 at 14:15
  • then unless you're a using main operation queue, you are making wrong assumptions if I understand you right. NSOperationQueue is built on top of gcd, that means that it manages threads and it is not guarantied which thread it will use each time you schedule operation in there even if it is serial. You should create local confinement mocs inside each operation, sharing single moc between nsoperations means that you're sharing moc between threads, which is very bad. Think you're 'lucky' not to get into any magic or crashes with your setup. – MANIAK_dobrii May 13 '15 at 14:25
  • In reality I'm sharing a MOC inside the same thread (moc stored in threadDictionary) I was wrong assuming that a specific NSOperationQueue uses always the same thread? (in any case isn't a problem, i create a different MOC for each thread, but have a bad memory impact) – Kappe May 13 '15 at 14:33
  • p.s Actually I'm sure that the caller's thread is still active because the NSOperation ends only when the network call ends. (original code amended) – Kappe May 13 '15 at 14:34
  • Yeah. And I'm not sure why you need to store your contexts. If you're using child parent mocs, child mocs are not updated according to parent or other child mocs after they're created, common solution is creating child moc every time, performing stuff, saving or not and dumping child moc, contexts are not expensive to create. Threads are not reliable, child mocs are one-time. – MANIAK_dobrii May 13 '15 at 14:39
  • If you're using some barriers to block your operation thread until completion block executes you can pass thread to completion blocks and perform code on supplied thread. The only thing is that you'll need to manage executing that code before operation ends somehow (i.e. in completion block you release barrier, operation ends, you've lost your thread). – MANIAK_dobrii May 13 '15 at 14:44
  • I'm not sure to have understood your last comment, I'm not calling the [self finish] inside the NSOperation until the network call ends and execute the completion block. Btw I'm storing the context because sometimes in the same thread i'm using it many times in different call, and instead pass the context around is easier store in in the thread and reuse it, but I'm evaluating your suggestion – Kappe May 13 '15 at 14:51

1 Answers1

2

AFNetworking executes the callback blocks on the main queue, as indicated by the answer to this question:

Are AFNetworking success/failure blocks invoked on the main thread?

If you are using GCD you aren't working with "threads" but instead with queues. The "thread" for the block operation will die once it has been completed, so when the callback is executed it will be impossible to have it be called in the same thread because it no longer exists. It is a bit unclear to me exactly what you are trying to achieve, but the answer to your question is no, you wan't be able to have the callback block execute on the same "thread".

I use quotes around thread to indicate that I mean it in the literal sense as it relates to the OS.

EDIT

It seems that the answer on the other SO question is no longer valid. Checking the source code, the completion blocks for an AFHTTPRequestOperation are executed on a concurrent queue. This is the relevant portion of the source:

https://github.com/AFNetworking/AFNetworking/blob/master/AFNetworking/AFHTTPRequestOperation.m#L107

Community
  • 1
  • 1
Aaron
  • 596
  • 1
  • 5
  • 15
  • Just tested (AFNetworking 2.0), no completionQueue set, POST operation Sequence: Main thread -> NSOperation (background thread 12) -> [_networkManager POST:...] completion block called on main thread, as usual – Kappe May 13 '15 at 14:35
  • Interesting, well the 2.5.3 is current release version of AFNetworking. Are you using exactly 2.0? – Aaron May 13 '15 at 14:59