How can i make sure in Objective-C, that a function only get called, until another function is ready?
Update:
-(void)reloadJsonFromServer {
[[Sync sharedObject] synchronise];
[self reload];
}
I've got this function.
The second function "reload" should only be called, if the first function is – in this case it's a singlton – is ready. Ready means, that the first function is no more longer running.
So you want to wait on the completion of an asynchronous method? There's a whole bunch of ways to do that.
Make the synchronise method itself call reload on your object when it finishes
dispatch_async the reload method and have it just wait until the other method populates some flag or data structure that you are waiting on before continuing (BOOL synchronised or similar). Note that if your reload method does anything with UIKit, though, then you need to run it on the main thread.
Change the way synchronise runs so it doesn't actually return to the caller until it's done synchronising, but then dispatch_async the reloadJsonFromServer method.
Change synchronise as in the third point, but instead of using dispatch_async, add both of the method calls to an NSOperationQueue as NSOperations, with reload dependent on the completion of synchronise. The operation queue will handle it after that.
Those are just a few, I'm sure other people can suggest more.
In the last few days, i've learnd something about notifications.
I think that is a good way too, to handle something like this. For more information about this look at this blog entry.
Custom Events in Objective-C
A bit late, but with NSNotification it can be handled.
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center addObserverForName:nil
object:nil
queue:nil
usingBlock:^(NSNotification *notification)
{
NSLog(#"%#", notification.name);
}];
Look at this: http://nshipster.com/nsnotification-and-nsnotificationcenter/
Related
I want to build an NSOperation that has a timeout of 10 seconds after it begins and could be ended by another thread at any point through an event. I also use an NSOperationQueue for managing more operations like this and it can only compute one at a time(maxConcurrentOperationCount = 1). For this I have thought about an implementation using dispatch_semaphore's as it follows:
#implementation CustomOperation
dispatch_semaphore_t semaphore;
-(void) main {
#autoreleasepool {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(shouldFinishWaiting:) name:#"myCustomEvent" object:nil];
semaphore = dispatch_semaphore_create(0);
[self doStuff];
dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC)));
[[NSNotificationCenter defaultCenter] removeObserver:self name:#"myCustomEvent" object:nil];
}
}
-(void) shouldFinishWaiting {
NSLog(#"[WatchOperation]: Should finish waiting! %#", self);
dispatch_semaphore_signal(semaphore);
}
#end
The problem I have is that once in many times when an user starts the application the first operation would not finish until the event gets triggered(and this could happen after 30 mins). The timeout would not be taken in consideration. I noticed this on logs from some users so I wasn't able to reproduce it. What could go wrong so that the dispatch_semaphore_wait fails to execute?
Later edit: I mistakenly thought that the -doStuff is async. It seems it is not.I replaced it with:
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ^{
[self doStuff];
});
, but the operation was already on the serial queue 'user initiated'. As I can see it creates another concurent thread, will that happen every time? Is this safe?
I don't think dispatch semaphore can go wrong. Maybe your -doStuff is taking too much time. Make sure you are doing the following:
1. The method [self doStuff]; is async and it dispatches to a DIFFERENT thread than the current one (dispatching to current thread doesn't really make sense if you want the 10 second timeout using the semaphore).
2. Make sure you keep checking for self.isCancelled in -doStuff.
Also, I would suggest that you take a slightly different design approach for your requirements (if I understand them correctly) --
1. Your NSOperation can always be cancelled from any external thread my calling cancel on the object, so there is no need for a complex NSNotification-based approach, just check for isCancelled and override the -cancel method.
2. For the 10 second timeout, you can use the semaphore approach, but just have a DIFFERENT thread doing the semaphore wait. That thread can then cancel your task from within after 10 seconds.
I assume your semaphore won't stay a global variable...
Depending on whether you are using 32 or 64 bit, NSEC_PER_SEC could be a 32 bit value, which overflows and turns into something negative when multiplied by 10. Replace 10 by 10.0. Might fix the problem, might not do anything at all.
#selector(shouldFinishWaiting:) should be #selector (shouldFinishWaiting). No colon because the method has no arguments.
My AppDelegate maintains a list of active window controllers to avoid ARC deallocating them too early. So I have a notification handler like this:
- (void) windowWillClose: (NSNotification*) notification {
[self performSelectorOnMainThread: #selector(removeWindowControllerInMainThread:)
withObject: windowController
waitUntilDone: NO];
}
- (void) removeWindowControllerInMainThread: (id) windowController {
[windowControllers removeObject: windowController];
}
I use the main thread because doing the handling on the notification thread risks deallocating the controller before it's ready.
Now, this works pretty well — except when there are animators currently running. I use animators in some places, through NSAnimationContext. I have looked at this QA, and the answer just isn't acceptable. Waiting for a while, just to get animation done, is really shoddy and not guaranteed to work; indeed it doesn't. I tried using performSelector:withObject:afterDelay, even with a larger delay than the current animation duration, and it still results in the animator running against nil objects.
What is the preferred way of doing controller cleanup like this? Not use NSAnimationContext but using NSAnimation instead, which has a stopAnimation method?
First, if some of your animations run indefinitely -- or for a very long time -- you're going to have to have a way to stop them.
But for things like implicit animations on views, you could simply use a completion method.
self.animating=YES;
[NSAnimationContext runAnimationGroup:^(NSAnimationContext *context){
[[v animator] setAlphaValue: 1];
} completionHandler:^{
self.animating=NO;
}];
Now, you only need to poll whether your animation is running and, if it's not running, proceed to close your window.
One nice way to do the polling is to set a timer with a fixed delay. If the animation is still running, just reset the timer and wait another interval.
Alternatively, you could send a notificaton from the completion handler.
I haven't used NSAnimationContext (always did this with NSAnimation, but mostly for historical reasons). But the typical way I like to managed things similar to this is to create short-lived retain loops.
Mark's answer is exactly the right kind of idea, but the polling is not required. The fact that you reference self in the completion handler means that self cannot deallocate prior to the completion handler running. It doesn't actually matter whether you ever read animating. ARC has to keep you around until the completion block runs because the block made a reference to you.
Another similar technique is to attach yourself to the animation context using objc_setAssociatedObject. This will retain you until the completion block runs. In the completion block, remove self as an associated object, and then you'll be free to deallocate. The nice thing about that approach is that it doesn't require a bogus extra property like animating.
And of course the final, desperate measure that is occasionally appropriate is to create short-lived self-references. For instance:
- (void)setImmortal:(BOOL)imortal {
if (immortal) {
_immortalReference = self;
}
else {
_immortalReference = nil;
}
}
I'm not advocating this last option. But it's good to know that it exists, and more importantly to know why it works.
I am looking for a better way to do this, if possible.
I have an asynchronous callback that updates a local sqlite database. I set a flag in a singleton variable (archiveUpdateComplete) when the update completes. I sleep in a do-while until the flag gets set to true, then I hydrate my tableview. Would like to remove sleep()! Thanks for any suggestions.
#define kBgQueue dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0)
- (void)viewDidLoad
{
dispatch_async(kBgQueue, ^{
//Hydrate word archive table view
do {
sleep(1.0);
} while ([sharedManager archiveUpdateComplete]==NO);
[self performSelectorOnMainThread:#selector(hydrateWordArchive) withObject:nil waitUntilDone:YES];
//Run custom activity indicator
dispatch_async(dispatch_get_main_queue(), ^{
[MBProgressHUD hideHUDForView:self.view animated:YES];
});
});
}
If you need to poll
Polling/sleeping is rarely necessary or good. As an alternative:
You can attach an NSTimer to the main thread's run loop.
The selector the timer calls can test [sharedManager archiveUpdateComplete]
if YES is returned, then
invalidate the timer
call [MBProgressHUD hideHUDForView:self.view animated:YES];
If you don't need to poll
There are a few immediate alternatives. Which you choose depends on what knows about what:
If your manager knows who to message following completion, then the manager can simply message it. If that must occur on the main thread you can use -[NSObject performSelectorOnMainThread:withObject:waitUntilDone:] to forward to the main thread. You may also see this approach with delegates. In the case of a singleton, it doesn't make a lot of sense to take this route.
If your manager does not know who is interested in the change/completion, your manager can post a NSNotification after the task has finished (on the current thread or from the main thread).
Key Value Observing (KVO) is another option.
Perhaps I'm missing something, but why don't you just use a completion callback for this?
In other words, you change your computation to "think" in terms of nested blocks. The first async block (on some concurrent queue) does the work of updating the database, and when it's done it dispatches another async block (to the same concurrent queue) which hydrates the tableview. Finally, from that block you dispatch_async yet another block on the main queue which updates the UI, since that's the only bit that needs to execute on the main queue.
Rather than poll, in other words, you want to chain your async operations. See COMPLETION CALLBACKS section of the man page for dispatch_async().
I came across this piece of code, and I can't quite figure out why the author did this. Take a look at this code:
someMethodStandardMethodUsingABlock:^() {
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:"notif" object:nil];
});
}];
I have a method with a completion block, and in this block a notification has to be posted. I don't quite understand why the dispatch_async on the main queue is necessary in this case. The block will already be run on the main thread, and even if it wasn't I don't think it would really matter would it? I would simply have written this:
someMethodStandardMethodUsingABlock:^() {
[[NSNotificationCenter defaultCenter] postNotificationName:"notif" object:nil];
}];
And it does work in my testing.
If you can help me shed some light on this, I'd really appreciate it!
Matt
These 2 sentences from the NSNotificationCenter Class Reference suggest a couple of possible reasons:
A notification center delivers notifications to observers
synchronously. In other words, the postNotification: methods do not
return until all observers have received and processed the
notification.
...
In a multithreaded application, notifications are always delivered in
the thread in which the notification was posted, which may not be the
same thread in which an observer registered itself.
So perhaps (a) the author doesn't want the code to block until all observers have processed the notification, and/or (b) he wants to ensure that the observer methods run on the main thread.
Sometimes you need to run methods that fire some execution asynchronously and return right away. E.g. some of the AppDelegate 'key' methods like applicationDidBecomeActive, or applicationDidEnterBackground, need to be executed and return quickly so the OS doesn't kill your app.
I don't know if that is the case of your question, but it is a possible explanation of the usage of dispatch_async.
I was having problems modifying a view inside a thread. I tried to add a subview but it took around 6 or more seconds to display. I finally got it working, but I don't know how exactly. So I was wondering why it worked and what's the difference between the following methods:
This worked -added the view instantly:
dispatch_async(dispatch_get_main_queue(), ^{
//some UI methods ej
[view addSubview: otherView];
}
This took around 6 or more seconds to display:
[viewController performSelectorOnMainThread:#selector(methodThatAddsSubview:) withObject:otherView
waitUntilDone:NO];
NSNotification methods - took also around 6 seconds to display the observer was in the viewController I wanted to modify paired to a method to add a subview.
[[NSNotificationCenter defaultCenter] postNotificationName:
#"notification-identifier" object:object];
For reference these were called inside this CompletionHandler of the class ACAccountStore.
accountStore requestAccessToAccountsWithType:accountType withCompletionHandler:^(BOOL granted, NSError *error) {
if(granted) {
// my methods were here
}
}
By default, -performSelectorOnMainThread:withObject:waitUntilDone: only schedules the selector to run in the default run loop mode. If the run loop is in another mode (e.g. the tracking mode), it won't run until the run loop switches back to the default mode. You can get around this with the variant -performSelectorOnMainThread:withObject:waitUntilDone:modes: (by passing all the modes you want it to run in).
On the other hand, dispatch_async(dispatch_get_main_queue(), ^{ ... }) will run the block as soon as the main run loop returns control flow back to the event loop. It doesn't care about modes. So if you don't want to care about modes either, dispatch_async() may be the better way to go.
It's likely because performSelectorOnMainThread:withObject:waitUntilDone: queues the message with common run loop modes. According to Apple's Concurrency Programming Guide, the main queue will interleave queued tasks with other events from the app's run loop. Thus, if there are other events to be processed in the event queue, the queued blocks in the dispatch queue may be run first, even though they were submitted later.
This article is a superb explanation to performSelectorOnMainThread vs. dispatch_async, which also answers the above question.
Did you try thePerformSelectorOnMainThread with waitUntilDone=YES
Eg:
Code:
[viewController performSelectorOnMainThread:#selector(methodThatAddsSubview:) withObject:otherView waitUntilDone:YES];
I think that might solve the issue as of why the PerformSelectorOnMainThread takes so long to respond.