The Receptionist Design Pattern (How to redirect to main thread?) - objective-c

The Receptionist Pattern is a design pattern that provides a way to redirect an event from one thread to another thread for it to be processed. The magic of using Key-Value Observing (KVO). More info: Receptionist
I understand the concept and the code. What I am struggling with is understanding how to accomplish the following:
"One common situation where the Receptionist pattern is useful is
key-value observing. In key-value observing, changes to the value of
an model object’s property are communicated to observers via KVO
notifications. However, changes to a model object can occur on a
background thread. This results in a thread mismatch, because changes
to a model object’s state typically result in updates to the user
interface, and these must occur on the main thread. In this case, you
want to redirect the KVO notifications to the main thread. where the
updates to an application’s user interface can occur."
Here is a snippet of what is described in the sample implementation.
The client object supplies the block code that updates the user
interface when it creates a receptionist object, as shown in Listing
4-4. Note that when it creates the receptionist object, the client
passes in the operation queue on which the block is to be executed, in
this case the main operation queue.
RCReceptionist *receptionist = [RCReceptionist receptionistForKeyPath:#"value" object:model queue:mainQueue task:^(NSString *keyPath, id object, NSDictionary *change) {
NSView *viewForModel = [modelToViewMap objectForKey:model];
NSColor *newColor = [change objectForKey:NSKeyValueChangeNewKey];
[[[viewForModel subviews] objectAtIndex:0] setFillColor:newColor];
}];
Simply asked, how does one acquire the main operation queue (aka 'mainQueue') off the main thread? Can someone help me connect the dots here please.

To grab the main operation queue:
[NSOperationQueue mainQueue];

Related

CoreData and NSManagedObject Context - private vs. main

Is there a difference between triggering a CoreData (CRUD) operation with a private context using performBlock and triggering such an operation using the main context in a dispatch_async background queue?
Is it simply the case that one should not use main context in a background thread?
Link to apple docs is here.
You should never use a main queue context in a background thread. That violates the thread confinement rules.
A main queue context should only every be accessed from the main queue (UI queue/thread) or from a performBlock. If you are needing to do a non-UI related task then you should create a private queue context and access it via a performBlock.
To confirm you have your Core Data threading correct you can turn on the -com.apple.CoreData.ConcurrencyDebug 1 runtime setting.

Why do I need to remove a notification observer before the object I'm observing is deallocated?

From https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSNotificationCenter_Class/Reference/Reference.html:
You must invoke removeObserver: or removeObserver:name:object: before any object specified by
addObserverForName:object:queue:usingBlock: is deallocated
Why does it matter that I stop observing before the object whose notifications I'm observing is deallocated? I understand why I as the observer need to stop observing if I'm going to disappear and the block depends on my existence, but I don't understand why the lifetime of the observed object matters. Am I misinterpreting this?
I understand why I as the observer need to stop observing if I'm going to disappear and the block depends on my existence, but I don't understand why the lifetime of the observed object matters.
I think that a possible explanation is the following.
addObserverForName:object:queue:usingBlock description says:
Adds an entry to the receiver’s dispatch table with a notification queue and a block to add to the queue, and optional criteria: notification name and sender.
"sender" in this context is just another name for the object parameter, which is described in the following terms:
The object whose notifications you want to add the block to the operation queue.
If you pass nil, the notification center doesn’t use a notification’s sender to decide whether to add the block to the operation queue.
So, object acts as a sort of filter: when a notification comes in, the notification center decides based on that value (if present) if the block must be added to the specified operation queue.
Now, consider this:
the observed object is deallocated without the observer to be removed;
a different object, also able to post notifications is created, and it happens it has the same address as the object deallocated at point 1;
now the observer will react to notifications posted by the second object.
I admit it is a pretty rare case, but it might happen, so you better code against it.
If u don't remove observer, it may leed to a situation when you already destroyed an object but it is still sent notifications - this will cause "message sent to deallocated instance" error

Alternatives to dispatch_get_current_queue() for completion blocks in iOS 6?

I have a method that accepts a block and a completion block. The first block should run in the background, while the completion block should run in whatever queue the method was called.
For the latter I always used dispatch_get_current_queue(), but it seems like it's deprecated in iOS 6 or higher. What should I use instead?
The pattern of "run on whatever queue the caller was on" is appealing, but ultimately not a great idea. That queue could be a low priority queue, the main queue, or some other queue with odd properties.
My favorite approach to this is to say "the completion block runs on an implementation defined queue with these properties: x, y, z", and let the block dispatch to a particular queue if the caller wants more control than that. A typical set of properties to specify would be something like "serial, non-reentrant, and async with respect to any other application-visible queue".
** EDIT **
Catfish_Man put an example in the comments below, I'm just adding it to his answer.
- (void) aMethodWithCompletionBlock:(dispatch_block_t)completionHandler
{
dispatch_async(self.workQueue, ^{
[self doSomeWork];
dispatch_async(self.callbackQueue, completionHandler);
}
}
This is fundamentally the wrong approach for the API you are describing to take. If an API accepts a block and a completion block to run, the following facts need to be true:
The "block to run" should be run on an internal queue, e.g. a queue which is private to the API and hence entirely under that API's control. The only exception to this is if the API specifically declares that the block will be run on the main queue or one of the global concurrent queues.
The completion block should always be expressed as a tuple (queue, block) unless the same assumptions as for #1 hold true, e.g. the completion block will be run on a known global queue. The completion block should furthermore be dispatched async on the passed-in queue.
These are not just stylistic points, they're entirely necessary if your API is to be safe from deadlocks or other edge-case behavior that WILL otherwise hang you from the nearest tree someday. :-)
The other answers are great, but for the me the answer is structural. I have a method like this that's on a Singleton:
- (void) dispatchOnHighPriorityNonMainQueue:(simplest_block)block forceAsync:(BOOL)forceAsync {
if (forceAsync || [NSThread isMainThread])
dispatch_async_on_high_priority_queue(block);
else
block();
}
which has two dependencies, which are:
static void dispatch_async_on_high_priority_queue(dispatch_block_t block) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), block);
}
and
typedef void (^simplest_block)(void); // also could use dispatch_block_t
That way I centralize my calls to dispatch on the other thread.
You should be careful about your use of dispatch_get_current_queue in the first place. From the header file:
Recommended for debugging and logging purposes only:
The code
must not make any assumptions about the queue returned, unless it
is one of the global queues or a queue the code has itself created.
The code must not assume that synchronous execution onto a queue is
safe from deadlock if that queue is not the one returned by
dispatch_get_current_queue().
You could do either one of two things:
Keep a reference to the queue you originally posted on (if you created it via dispatch_queue_create), and use that from then on.
Use system defined queues via dispatch_get_global_queue, and keep a track of which one you're using.
Effectively whilst previously relying on the system to keep track of the queue you are on, you are going to have to do it yourself.
Apple had deprecated dispatch_get_current_queue(), but left a hole in another place, so we still able to get current dispatch queue:
if let currentDispatch = OperationQueue.current?.underlyingQueue {
print(currentDispatch)
// Do stuff
}
This works for main queue at least.
Note, that underlyingQueue property is available since iOS 8.
If you need to perform the completion block in the original queue, you also may use OperationQueue directly, I mean without GCD.
For those who still need in queue comparing, you could compare queues by their label or specifies.
Check this https://stackoverflow.com/a/23220741/1531141
This is a me too answer. So I will talk about our use case.
We have a services layer and the UI layer (among other layers). The services layer runs tasks in the background. (Data manipulation tasks, CoreData tasks, Network calls etc). The service layer has a couple operation queues to satisfy the needs of the UI layer.
The UI layer relies on the services layer to do its work and then run a success completion block. This block can have UIKit code in it. A simple use case is to get all messages from the server and reload the collection view.
Here we guarantee that the blocks that are passed into the services layer are dispatched on the queue on which the service was invoked on. Since dispatch_get_current_queue is a deprecated method, we use the NSOperationQueue.currentQueue to get the caller's current queue. Important note on this property.
Calling this method from outside the context of a running operation
typically results in nil being returned.
Since we always invoke our services on a known queue (Our custom queues and Main queue) this works well for us. We do have cases where serviceA can call serviceB which can call serviceC. Since we control where the first service call is being made from, we know the rest of the services will follow the same rules.
So NSOperationQueue.currentQueue will always return one of our Queues or the MainQueue.

Difference between Main Queue / Current Queue & Main Thread / Background Thread in this case?

I'm executing the following method :
MotionHandler.m
-(void)startAccelerationUpdates
{
[motionManagerstartDeviceMotionUpdatesToQueue:[NSOperationQueue mainQueue]withHandler:^(CMDeviceMotion *motion, NSError *error){.....}
}
on a background thread, as follows:
[currentMotionHandler performSelectorInBackground:#selector(startAccelerationUpdates) withObject:nil];
But the above method uses the main Queue (which is on the main thread) to perform the necessary updates even though I'm calling it on a background thread.. So are acceleration updates being performed on a background thread or on the main thread, I'm confused..?
What's even more interesting is that when I call the above method on background thread again, but this time using the current Queue, I get no updates. Could someone please explain the difference between running something on :
1. a background thread but on the main queue
2. a background thread but on the current queue
3. the main thread but on the main queue
4. the main thread but on the current queue
in the current implementation? Thank you!
I'll give it a shot. First, without being told by the NSOperationQueue class reference, we could not infer anything about what thread the 'mainQueue' would run on. Reading it we see that in fact that queue runs its operations on the mainThread, the one the UI uses, so you can update the UI in operations posted to that queue. Although it doesn't say it, these operations must be serial, due to them being executed by the runLoop (its possible they can get preempted too, not 100% sure of that).
The purpose for currentQueue is so that running operations can determine the queue they are on, and so they can potentially queue new operations on that queue.
a background thread but on the main queue
Not possible - the NSOperation's mainQueue is always associated with the mainThread.
a background thread but on the current queue
When you create a NSOperationQueue, and add NSOperations to it, those get run on background threads managed by the queue. Any given operation can query what thread its on, and that thread won't change while it runs. That said, a second operation on that queue may get run on a different thread.
the main thread but on the main queue
See 1)
the main thread but on the current queue
If you queue an operation to the mainQueue (which we know is always on the mainThread), and you ask for the currentQueue, it will return the mainQueue:
[NSOperationQueue currentQueue] == [NSOperationQueue mainQueue];
You are confusing queues with threads. Especially since NSOpertionQueue has been rewritten to use GCD, there is little connection between queues and specific threads (except for the special case of the main thread).
Operations/blocks/tasks - whatever you want to call them - are inserted into a queue, and "worker thread(s)" pull these off and perform them. You have little control over which exact thread is going to do the work -- except for the main queue. Note, this is not exactly right, because it's a simplification, but it's true enough unless you are doing something quite advanced and specific.
So, none of your 4 scenarios even make sense, because you can't, for example, run something on "a background thread but on the main queue."
Now, your method startAccelerationUpdates specifically tells the CMMotionManager to put your handler on the main queue. Thus, when startAccelerationUpdates is called, it gets run in whichever thread it's running, but it schedules the handler to be executed on the main thread.
To somewhat complicate things, you are calling the startAccelerationUpdates method by calling performSelectorInBackground. Again, you don't know which thread is going to actually invoke startAccelerationUpdates, but it will not be the main thread.
However, in your case, all that thread is doing is calling startAccelerationUpdates which is starting motion updates, and telling them to be handled on the main thread (via the main queue).
Now, here's something to dissuade you from using the main queue to handle motion events, directly from the documentation...
Because the processed events might arrive at a high rate, using the main operation queue is not recommended.
Unfortunately, your statement
What's even more interesting is that when I call the above method on
background thread again, but this time using the current Queue, I get
no updates.
does not provide enough information to determine what you tried, how you tried it, or why you think it did not work. So, I'll make a guess... which may be wrong.
I'll key on your use of the current Queue.
I assume you mean that you substitute [NSOperationQueue mainQueue] with [NSOperationQueue currentQueue].
Well, let's see what that does. Instead of using the main queue, you will be using "some other" queue. Which one? Well, let's look at the documentation:
currentQueue
Returns the operation queue that launched the current
operation.
+ (id)currentQueue
Return Value
The operation queue that started the operation or nil if the queue could not be determined.
Discussion
You can use this method from within a running operation
object to get a reference to the operation queue that started it.
Calling this method from outside the context of a running operation
typically results in nil being returned.
Please note the discussion section. If you call this when you are not running an operation that was invoked from an NSOperationQueue, you will get nil which means there will be no queue on which to place your handler. So, you will get nothing.
You must specify which queue is to be used, if you want to use an NSOperationQueue other than the main queue. So, if that's the route you want to go, just create your own operation queue to handle motion events, and be off!
Good Luck!

Matching CoreData save notifications for callbacks

I'd like to pick someone's brain on this. I have a dedicated save NSManagedObjectContext and GCD queue from which I operate on it. And whenever new data comes into my app I save it on that context and merge the changes into the main context. My problem arises in telling the main thread what just happened. Right after I call save my current context is now up-to-date, but if I fire a method in the main context it's context isn't. If I wait for the NSManagedObjectContextDidSave notification, and I save three times, I now have three queued delegate calls but no way to match them to the notifications coming in. Does anyone know of a good way to get around this?
EDIT
What I ended up doing was creating a new context for each save operation and attaching a block to be called when the save notification arrived. It looks like this, http://pastie.org/2068084
From your answer to my comment above, I see that you pass along the managedObjectContext in the notification. I'm not that confident about asynchronous stuff yet, but I do think that you're violating some concurrency rule, if I correctly interpret this quote from the NSManagedObjectContext Class Reference:
Concurrency
Core Data uses thread (or serialized queue) confinement to protect managed objects and managed object contexts (see “Concurrency with Core Data”). A consequence of this is that a context assumes the default owner is the thread or queue that allocated it—this is determined by the thread that calls its init method. You should not, therefore, initialize a context on one thread then pass it to a different thread. Instead, you should pass a reference to a persistent store coordinator and have the receiving thread/queue create a new context derived from that.
I'd say, try passing along the persistent store coordinator in the notification and recreate the managed object context in the block.
I'm not sure what you mean by, " ...if I fire a method in the main context it's context isn't. If I wait for the NSManagedObjectContextDidSave..." That implies that you are not waiting until the contexts have been merged. If so, that is why you can't access the data, it's just not in the front context.
Do you call mergeChangesFromContextDidSaveNotification: from the front context after it receives the notification?