For an iCloud plugin I'm writing, I subscribe my iCloud manager class to these iCloud NSMetaDataQuery observers:
// Add a predicate for finding the documents
NSString* filePattern = [NSString stringWithFormat:#"*.%#", #"*"];
self.metadataQuery = [[NSMetadataQuery alloc] init];
// Before starting to query, it is required to set the search scope.
arrayWithObject:NSMetadataQueryUbiquitousDocumentsScope]];
// It is also required to set a search predicate.
[self.metadataQuery setPredicate:[NSPredicate predicateWithFormat:#"%K LIKE %#", NSMetadataItemFSNameKey, filePattern]];
// Listen for the second phase live-updating
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(queryDidReceiveNotification:) name:NSMetadataQueryDidUpdateNotification object:nil];
// Listen for the first phase gathering
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(queryIsDoneGathering:) name:NSMetadataQueryDidFinishGatheringNotification
object:nil];
[self.metadataQuery startQuery];
The problem is that none of these selectors are actually ever called back, not even once, and I especially need the NSMetaDataQueryDidUpdateNotification to track upload/download progress of files in the cloud.
An odd thing is that I had this working the other day, but somehow it just stopped working and I've starred myself blind in trying to figure out what the problem actually is. By subscriping to the NSMetadataQueryDidStartGatheringNotification I can see that it does start, but it's like it never finishes. It is quite weird.
I was wondering if anyone have any clue at all as what to be wrong with the above code? Or where else I can look for the problem.
Thank you for your time :)
Make sure you start the NSMetadataQuery in the main thread. Back then this requirement was not documented.
This was quite the secret to dig up. I don't know if you've given up by now, but at long last, I might be able to help.
It turns out that for some (all?) C++ app configurations, there is a message pump that doesn't run automatically. In my app, I finally started getting my expected callbacks after placing a loop like this following my [m_query startQuery] call:
// make it look syncronous for now
while( m_state != finished )
{
Check_For_Interruption();
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:timeslice]]; //<--**this line is the key**
}
where the callbacks are setup to correctly update the m_state variable.
My example just blocks, using it's own thread timeslice to run the loop (unless interrupted by a timeout being reached), but this could also be set up in an asynchronous way.
Another thing to note for those who go overboard on legacy support:
This method did cause the app to start leaking mach ports on OS X 10.9 and older. Haven't seen any problems on anything newer than that, though.
Related
I want to create a background application that keep track of the current active application (active window). I manage to get the active application by using GetFrontProcess but now I need to keep track of it so I can know when it changes.
I thought of two ways
1. Setting a Timmer and recheck the variable each time the timer triggers (Should I create a thread?)
2. Creating a dedicated thread, check the variable in a loop and sleep for a few Milliseconds after every iteration to avoid overhad.
Efficiency is important to me as this should be a non disturbing background thread.
Do you think those methods are right? Any efficiency considerations? Would love to hear about different ideas that are more efficient.
Thanks for your help
Gil
You could observe the NSWorkspace notification NSWorkspaceDidActivateApplicationNotification.
eg:
[[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self
selector:#selector(appActivated:)
name:NSWorkspaceDidActivateApplicationNotification
object:nil];
and have a method:
-(void)appActivated:(NSNotification*) notification {
NSRunningApplication* currentApp = [notification.userInfo valueForKey:NSWorkspaceApplicationKey];
....
}
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.
I'm new to Objective-C and programming in general but I'm beginning to grasp the syntax and have a mostly working application however I'm struggling with one part. I'd like to be able to use the application I'm writing to monitor the activity of another application, namely wether it's open or not.
Ideally when a user clicks a button it will launch remote desktop client and then monitor when remote desktop client closes. I want to know when it closes so that I can either bring my application to the forefront or to restart the computer. Mostly my problems revolve around watching for when remote desktop client closes. Here's what I was thinking of trying:
do {
NSArray* apps = [NSRunningApplication runningApplicationsWithBundleIdentifier:#"com.microsoft.rdc"];
} while ([apps count] >= 1);
The problem with the approach you've posted is that that while loop will block the main thread, preventing your application from doing anything else. You could run that on a background thread to prevent that problem, but that's probably not the best approach.
Instead, take a look at the NSWorkspace class's notifications. One of them is NSWorkspaceDidTerminateApplicationNotification. You should be able to do something like this:
// Put this part in your app delegate's applicationDidFinishLaunching: method, or some other appropriate place
NSNotificationCenter *nc = [[NSWorkspace sharedWorkspace] notificationCenter];
[nc addObserver:self selector#selector(anotherAppDidTerminate:) name:NSWorkspaceDidTerminateApplicationNotification object:nil];
- (void)anotherAppDidTerminate:(NSNotification *)notification
{
NSRunningApplication *app = [[notification userInfo] objectForKey:NSWorkspaceApplicationKey];
if ([app.bundleIdentifier isEqualToString:#"com.microsoft.rdc"]) {
// RDC closed, so do whatever it is you want to here
}
}
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/