Objective-C completion block causing extra method call? - objective-c

Here's a weird one. My application sends a shutdown message to an object controlling a hardware device, with a completion block as an argument. The shutdown message returns a BOOL, depending on whether it was able to complete shutdown immediately. So YES means it's done now, NO means it will invoke the completion handler when it's done later.
Here's the main controller code:
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
{
BOOL shutdownNow = [theStoker shutdownWithCompletionHandler:^(void)
{
NSLog(#"applicationShouldTerminate: completionBlock");
[[NSRunningApplication currentApplication] terminate];
}];
if (!shutdownNow)
{
NSLog(#"applicationShouldTerminate: waiting for shutdown");
return NSTerminateCancel;
}
return NSTerminateNow;
}
Here's the device controller code:
- (BOOL)shutdownWithCompletionHandler:(void (^)(void))handler
{
if (busy)
{
self.completionBlock = handler;
[self stopDevice];
NSLog(#"shutdownWithCompletionHandler: Wait for reset");
return NO;
}
NSLog(#"Stoker: shutdownWithCompletionHandler: shutdown now");
return YES;
}
The weird part is that the shutdown messages to be sent twice. These are the NSLog messages:
shutdownWithCompletionHandler: Wait for reset
applicationShouldTerminate: waiting for reset
applicationShouldTerminate: completionBlock
shutdownWithCompletionHandler: shutdown now
So after the completion block runs, I end up back in the device object's shutdownWithCompletionHandler: method. Why?
Edit: Hmmm. Does the [[NSRunningApplication currentApplication] terminate]; cause the
applicationShouldTerminate: to get called again? I think it must. Is there a better way to exit the app in the completion handler?

Since you have found the underlying cause of your problem (calling terminate causes applicationShouldTerminate:), here is how to avoid it.
Instead of canceling the termination, return NSTerminateLater when it will shutdown later. Then, your completion block should call [NSApp replyToApplicationShouldTerminate:YES] to trigger the termination, without calling applicationShouldTerminate: again.
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender {
BOOL shutdownNow = [theStoker shutdownWithCompletionHandler:^(void) {
NSLog(#"applicationShouldTerminate: completionBlock");
[NSApp replyToApplicationShouldTerminate:YES];
}];
if (!shutdownNow) {
NSLog(#"applicationShouldTerminate: waiting for shutdown");
return NSTerminateLater;
}
return NSTerminateNow;
}

Related

Xcode Objective C while loop cannot access global variables

I'm very new to Xcode and Objective C. So most probably this is going to be an easy question (:
I'm trying to create an app that would do something if Caps Lock key is pressed.
And I need to see if Caps lock is pressed even if app is not in focus.
I've managed to check Caps Lock state even if my app is not in focus.
But I have an issue with getting out from while loop.
I cannot understand why my app cannot see what is happening to Caps lock key once it gets inside while loop.
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
_spam1 = NO;
[NSEvent addGlobalMonitorForEventsMatchingMask:NSFlagsChangedMask handler:^(NSEvent* event){
[NSThread sleepForTimeInterval:1];
if(event.keyCode == 0x39){ //CAPS LOCK key was pressed
if([event modifierFlags] & NSAlphaShiftKeyMask){
_spam1 = YES;
NSLog(#"caps lock is on");
} else {
_spam1 = NO;
NSLog(#"caps lock is off”);
}
//if I comment this part
//I can see if caps lock is on or off just fine
while(_spam1){
NSLog(#"Spam %#" , event);
NSLog(#"Spam %lu" , [event modifierFlags] & NSAlphaShiftKeyMask);
[NSThread sleepForTimeInterval:1];
if([event modifierFlags] & NSAlphaShiftKeyMask){
//_spam1 = YES;
} else {
_spam1 = NO;
NSLog(#“stop while loop”);
break;
}
}
}
}];
}
Work with framework, not agains:
Create a new subclass of NSView and override one of following methods. Set the new subclassed view in Interface builder. ( read more in Event handling guide )
So your code should look like this:
//multiKey handling
unichar SPACE_CHARACTER = 0x0020;
- (BOOL)performKeyEquivalent:(NSEvent *)theEvent
{
id responder = [[self window] firstResponder];
if (responder != self)
{
return [super performKeyEquivalent:theEvent];
}
NSUInteger numberOfPressedCharacters = [[theEvent charactersIgnoringModifiers] length];
NSEventType eventType = [theEvent type];
if (eventType == NSKeyDown && numberOfPressedCharacters == 1)
{
unichar key = [[theEvent charactersIgnoringModifiers] characterAtIndex:0];
if (key == SPACE_CHARACTER)
{
[self spaceBarPressed];
return YES;
}
}
return NO;
}
or
//single key handling (no modifiers like shift, ctrl...)
- (void)keyDown:(NSEvent *)theEvent
{
// code belongs here
}
Maybe I'm missing something, but why do you have the while loop at all, as your code comment says it works without it?
In outline what is happening is as follows:
Your call to addGlobalMonitorForEventsMatchingMask requests the system to call the block you pass every time an event occurs (that matches). This is done asynchronously, and for this to happen you must be going around your event loop.
The standard application model is event driven, the system sets up the main event loop and calls your code to handle the various events - including any required calls to your event monitoring block. After your code handles the event it returns, the event loop goes around and obtains the next event, calls your code to handle it, etc., etc.
When your block is called it is passed a reference to the event it needs to process. In a single call that event will not be changed asynchronously by the system - the system obtains the event as part of the event loop processing and calls your block, until that call returns the event loop cannot continue.
So after all that preamble what happens with your while loop is the call to sleepForTimeInterval causes your application to pause within the call to the block - the block does not return and so its caller, the event loop, does not continue either. On waking up your loop continues to process exactly the same event it was passed when called, your block then continues around its loop getting nowhere...
HTH

iOS: Waiting for API Completion Block and returning the result

Using inheritance.
I have a child class that calls a method in the parent class that runs calls the server API.
-(IBAction)buttonPressed
{
[self methodInParentClassThatCallsTheAPI:param];
// This is where I would like the call back
if (success from the server API) // do something with the UI
else if (failure from the server API) // do so something else with the UI
}
Parent Class:
- (void)methodInParentClassThatCallsTheAPI:(NSString *)param
{
//The method below calls the server API and waits for a response.
[someServerOperation setCompletionBlockWithSuccess:^(param, param){
// Return a success flag to the Child class that called this method
} failure:^(param, NSError *error){
// Return a failure flag to the Child class that called this method
}
}
How can I accomplish this with a block? Is there a better way to do this other than the block? Code example please
Create a completion block on methodInParentClass like this:
- (void)methodInParentClassThatCallsTheAPI:(NSString *)param completionBlock:(void (^)(BOOL success))completionBlock;
Then fire it inside the block success/failure with the appropriate value like this:
completionBlock(YES);
EDIT: By the way, please note that the return may not be on the main thread so if you plan to do a UI update you can fire the return block with a dispatch_async(dispatch_get_main_queue, ^{});
EDIT2: Since you seem to suggest this is the result of a button tap if your child is a VC waiting on the return just remember that this block will return async (obviously since that's what it is designed for) so if you need to hold the user for whatever reason you'll need to have the main thread display a loading indicator of some sort and hold user input events. Just remember that if you don't the UI will continue responding when before the completion block fires so at the very least you will want to disable the button so the user can't multi-press it and then you can reenable it when the completion block fires from the child VC.
EDIT3: Ok here is the calling code.
-(IBAction)buttonPressed
{
[self methodInParentClassThatCallsTheAPI:param withCompletionBlock:^(BOOL success){
if (success) {
// do something with the UI
} else {
// Do something else
}
}];
}

beginBackgroundTaskWithExpirationHandler with a current thread

beginBackgroundTaskWithExpirationHandler creates a new thread when we execute something.
Can I execute something using an existing thread with this?
Because new thread generated by beginBackgroundTaskWithExpirationHandler causes some problems to my application when it is resumed. So I passed an instance of an existing thread to beginBackgroundTaskWithExpirationHandler and called the required methods using the existing thread. Is it ok to use existing threads inside beginBackgroundTaskWithExpirationHandler ?
Will it cause any problems?
- (void)applicationDidEnterBackground:(UIApplication *)application {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
IOSMobilePOSApplication *app = [IOSMobilePOSApplication getInstance];
if ([app keepAliveMessage]) {
if([[UIDevice currentDevice] respondsToSelector:#selector(isMultitaskingSupported)])
{
[Logger log:#"Multitasking Supported"];
background_task = [application beginBackgroundTaskWithExpirationHandler:^ {
[Logger log:#"Background maximum time exeeded."];
IOSMobilePOSApplication *iosMobileApplication = [IOSMobilePOSApplication getInstance];
[[iosMobileApplication getKeepAliveManager] stop];
//Clean up code. Tell the system that we are done.
[application endBackgroundTask: background_task];
background_task = UIBackgroundTaskInvalid;
}];
//To make the code block asynchronous
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//### background task starts
[Logger log:#"Running in the background"];
IOSMobilePOSApplication *iosMobileApplication = [IOSMobilePOSApplication getInstance];
[[iosMobileApplication getKeepAliveManager] setEnabled:true];
[[iosMobileApplication getKeepAliveManager] performSelector:#selector(run) onThread:[[iosMobileApplication getKeepAliveManager] runingThread] withObject:NULL waitUntilDone:NO];
isStarted = true;
//#### background task ends
//Clean up code. Tell the system that we are done.
[application endBackgroundTask: background_task];
background_task = UIBackgroundTaskInvalid;
});
}
else
{
[Logger log:#"Multitasking Not Supported"];
}
}
}
here
[[iosMobileApplication getKeepAliveManager] run]; causes new thread to start and it causes thread synchronization problems in my code. So I added the code in place of above mentioned line.
[[iosMobileApplication getKeepAliveManager] performSelector:#selector(run) onThread:[[iosMobileApplication getKeepAliveManager] runingThread] withObject:NULL waitUntilDone:NO];.
Will this cause any problems when the app goes background?
It’s probably some mistake, because -beginBackgroundTaskWithExpirationHandler: doesn’t create any threads. It only marks the beginning of some long-running task you’re about to start or just started.
This method can be called from any thread. The expiration handler that it takes as a parameter is called on the main thread. This handler is used to clean up and mark the end of the long-running task you’re doing.

Recursion with blocks in objective-c

I am receiving EXC_BAD_ACCESS signal in my iOS application when doing a recursion that involves objective-c blocks. Here is the simplified code:
- (void)problematicMethod:(FriendInfo*)friendInfo onComplete:(void(^)(NSString*))onComplete1 {
[self doSomethingWithFriend:friendInfo onComplete:^(Response* response) {
switch (response.status) {
case IS_OK:
onComplete1(message);
break;
case ISNT_OK:
// Recursively calls the method until a different response is received
[self problematicMethod:friendInfo onComplete:onComplete1];
break;
default:
break;
}
}];
}
So basically, the problematicMethod, in this simplified version, calls doSomethingWithFriend:onComplete:. When that method finishes (onComplete), and if everything was ok, the original onComplete1 block gets called, and this works fine.
But if something went wrong, problematicMethod needs to be called again (the recursion part), and when this happens for the first time, I immediately get EXC_BAD_ACCESS signal.
Any kind of help would be greatly appreciated.
How are you creating your block? Remember that you have to move it from stack to heap.
Example:
void(^onCompleteBlock)(NSString*) = [[^(NSString* param) {
//...block code
}] copy] autorelease];
[self problematicMethod:friendInfo onCompleteBlock];
If response.status value is ISNT_OK you never finish calling recursively the function.

NSThread Not Loading Selector Method

In the initialization method of a class I am declaring the thread as such:
NSThread* myThread = [[[NSThread alloc] initWithTarget:self selector:#selector(m_run_thread) object:nil] autorelease];
[myThread start];
I also have a boolean value which is set to NO. Later on in the code I set the boolean value to YES.
bool_run_progress_thread = YES;
The contents of the method m_run_thread is as follows:
-(void) m_run_thread
{
if (bool_run_progress_thread)
{
//do processing here
}
bool_run_progress_thread = NO;
}
The problem is that the method m_run_thread is never being accessed. What am I doing wrong?
P.S. I have also tried to set up the Thread using the following (and older)method:
[NSThread detachNewThreadSelector:#selector(m_run_thread)
toTarget:self
withObject:nil];
... but to no avail as well.
"...and I am only getting it to show once" Yes, that's exactly how it should be. After being started, a thread runs once from its start to its end (ignoring errors here for the moment), and having reached the end, the thread is essentially dead and gone.
If you want the thread to repeat its execution, you have to prepare for that yourself:
- (void) m_run_thread
{
for (;;)
{
if (bool_run_progress_thread)
{
//do processing here
bool_run_progress_thread = NO;
}
}
}
But there is still a lot wrong with this code: essentially, when run, the code forms a busy waiting loop. Assuming, that bool_run_progress_thread is only ever true for short periods of time, the background thread should be sleeping most of the time. Insead, if you try the code as its stands, it will instead consume CPU time (and lots of it).
A better approach to this would involve condition variables:
#class Whatsoever
{
NSCondition* cvar;
BOOL doProgress;
...
}
...
#end
and
- (void) m_run_thread
{
for (;;)
{
[cvar lock];
while (!doProgress)
{
[cvar wait];
}
doProgress = NO;
[cvar unlock];
... do work here ...
}
}
and in order to trigger the execution, you'd do:
- (void) startProgress
{
[cvar lock];
doProgress = YES;
[cvar signal];
[cvar unlock];
}
Doing things this way also takes care of another subtle problem: the visibility of the changes made to the global flag (your bool_run_progress_thread, my doProgess). Depending on the processor and its memory order, changes made without special protection might or might not become (ever) visible to other threads. This problem is taken care of by the NSCondition, too.