When creating an NSOperation and putting it into an NSOperationQueue, I never see main() being called. Only start() is getting called. I'm not doing anything fancy, really. As a simple test, I wrote this:
NSOperationQueue *testOperationQueue = [[NSOperationQueue alloc] init];
MyTestOperation *testOperation = [[MyTestOperation alloc] init];
[testOperationQueue addOperation:testOperation];
in MyTestOperation.m:
- (void)main
{
NSLog(#"testing if main is getting called");
}
- (void)start
{
NSLog(#"testing if start is getting called");
}
MyTestOperation.h looks like this:
#import <Foundation/Foundation.h>
#interface MyTestOperation : NSOperation
#end
Am I missing something obvious?
[Edit Note: I actually meant non concurrent, not concurrent (as written in the previous title).]
I played around with your code and noticed that if I commented out your start method entirely, the main method would run. After reading the documentation for start more closely, I found this line:
The default implementation of this method updates the execution state of the operation and calls the receiver’s main method.
Thus, by overriding start with an implementation that didn't call main, you effectively short-circuited the operation and never allowed it to actually begin working! Therefore, you either need to explicitly call main within your start method, or you need to eliminate start all-together if you're not actually using it to do setup/update execution state/etc.
Related
This is not a trivial question asked here in StackOverFlow before, at least I haven’t found anything similar, of course I also googled it and read most of high ranked results.
BTW, if any folks here don't feel comfortable with Objective C’s block syntax, visit this page please
http://fuckingblocksyntax.com ,
before throwing any block related issues.
1st part of my question is: the background of declaration of block-parameter, as well as invoking a method which has a block-parameter ( in many cases, a completionBlock )
The “calleE-method" in MyWorker class:
… ...
#implementation MyWorker
-(void) aWorkerMethodNeedsABlockInput: ((void)(^)( NSObject *, double )) blockParam
{
NSObject *anObj=[[ NSObject alloc] init];
double *aDouble;
[self retrieveTimeConsumingResults: anObj withNumberOfTry: aDouble ];
blockParam ( anObj, * aDouble );
}
#end
The “calleR-method" in MyManager class:
#interface myManager()
#property (nonatomic) MyWorker * mWorker;
#property (nonatomic, copy) (void)(^mBlockProperty)( NSObject *, double );
#end
#implementation MyManager
-(void) aManagerMethodWhoCallsWorkerWithCompletionHandler
{
(void)(^ valBlock )( NSObject *, double ) = ^(void)( NSObject * realObj, double realDouble )
{
[realObj performSelector:#SEL( aSelector) withObject: #(realDouble) afterDelay: aTimeInterval];
} ;
self.mBlockProperty=[valBlock copy];
[self.mWorker aWorkerMethodNeedsABlockInput : self.mBlockProperty];
}
#end
above sudo-code was the NORMAL way, in our custom code, of storing a block inside property, declaring a block parameter and also offering block’s arguments in CALLEE; providing block definition and also “consuming” block’s arguments in the CALLER. I keep 'void' returnType in writing for clarity of block-syntax. Correct my writing if I did wrong, please!
2nd part of my question:
the routine usage of
- (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)())completionHandler {
NSLog(#"Handle events for background url session");
self.backgroundSessionCompletionHandler = completionHandler;
}
then later
- (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session {
WebAppDelegate *appDelegate = (WebAppDelegate *)[[UIApplication sharedApplication] delegate];
if (appDelegate.backgroundSessionCompletionHandler) {
void (^completionHandler)() = appDelegate.backgroundSessionCompletionHandler;
appDelegate.backgroundSessionCompletionHandler = nil;
completionHandler();
}
NSLog(#"All tasks are finished");
}
the background callback via the daemon works in above pattern based on NSURLSession framework, right? I did it many times, not a problem on applying such pattern.
Which I have been wondering for a long time is:
What is really inside the definition of the completionHandler parameter of “handleEventsForBackgroundURLSession:” method, when the method is invoked from a block-property storage? < at the time when “ completionHandler();” is executed >
I have never seen any sample/demo which put/copy any block-of-code into completionHandler... or I wish to know too much?
What is really inside the definition of the completionHandler parameter of “handleEventsForBackgroundURLSession:” method, when the method is invoked from a block-property storage? < at the time when “ completionHandler();” is executed > I have never seen any sample/demo which put/copy any block-of-code into completionHandler... or I wish to know too much?
If I understand your question correctly, you are asking what implementation is inside the block that is passed to an application's implementation of the UIApplicationDelegate method application:handleEventsForBackgroundURLSession:completionHandler: by the system.
application:handleEventsForBackgroundURLSession:completionHandler: is invoked (indirectly) by an external service process. When an application uses NSURLSession to create a background session, that session is managed by that system service. That service does the actual background transfer and notifies UIKit/Foundation and in turn your application through a mechanism called XPC. XPC is widely used by MacOS developers, but at this time is not directly available to iOS applications - however many of the APIs and services used by developers on iOS are actually communicating with XPC services.
In the case of application:handleEventsForBackgroundURLSession:completionHandler:, the block passed to the completionHandler parameter is an opaque callback. The background transfer service needs to know when your application is done handling events for the session. Invoking that block informs the service that the application has completely processing of this set of events and the daemon can move on.
The block is created and owned by the system and as such an application should not attempt to modify or change it (other than copying the block, which is the right thing to do!). Applications should also not provide their own completion blocks - a developer-provided block would have no way to inform the transfer service of completion unless it wrapped the block passed to completionHandler: itself.
The background transfer service and NSURLSession were introduced in iOS 7. If you are writing a third party framework or library it can be very beneficial to take advantage of the service, but the framework must provide a way to handle events for any background session it owns. Perhaps because of this only a few third party libraries seem to support background transfers. Supporting this is not difficult to do - the library only needs a method to indicate ownership of the session, and a method to take the completion block and handle the events:
- (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)())completionHandler {
if ([someCloudFrameworkObject canHandleEventsForSessionWithIdentifier:identifier]){
[someCloudFrameworkObject handleEventsForBackroundSessionWithIdentifier:identifier completionHandler:completionHandler];
}
}
We had a bug, and it destroys the looks of our UI, some of the UI elements overlap, or has been added to the subview twice. the bug is hardly reproduced so its hard to fix it. Now I thought of the reason, and probably the method that changes the UI are being called twice at the same time. And I was correct, I tried to create the bug programatically.
We have a bug which is caused by a method being accessed by different threads at the same time. To emulate this problem, and better understand it. see codes posted below.
When I do this, updatePresence Method call, my program works perfectly
ViewController.m
-(void)loadConversationScreen{
[conversationController updatePresence];
}
But when I do this, something goes wrong with my program
ViewController.m
-(void)loadConversationScreen{
[conversationController performSelectorInBackground:#selector(updatePresence) withObject:nil];
[conversationController updatePresence];
}
This is because the method is being accessed at the same time and and the instance of my UIView is being accessed/changed also at the same time.
How do I PROPERLY stop 2 threads from using a method at the same time?
How do I properly handle it in IOS(if there is no proper way, what are the work arounds), are there built in locks or somekind?
My app should support ios 4.0 and up
Advance thanks to all for your help.
The best thread lock for you is #sycnhronized(object) {}. This means only one thread can enter the code at a time. The object passed in is used to perform the lock; only one thread can enter a block protected by a particular object's synchronized at a time. The others will wait. This can be any Objective-C object, even a NSString.
Typically, you'd use whatever object you're trying to protect from multiple threads. You probably want #synchronized(self) {}:
-(void)updateVariables {
#synchronized(self) {
_foo = 1;
_bar = 2;
}
}
#sycnhronized is re-entrant in the sense that the same thread can call #sycnhronized as deeply as it wants, for instance:
- (void)a {
#synchronized(self) {
// entered "immediately" if called from b, where the #synchronized has
// already been called
_foo = _foo + 1;
}
}
- (void)b {
#synchronized(self) {
[self a];
}
}
For posterity and because I already typed it before reading your clarification, if you really cared only about updating the UI, you'd want to force your call over to the main thread instead like this:
- (void)someTask {
dispatch_async( dispatch_get_main_queue(), ^{
[self updateUI];
});
}
- (void)updateUI {
NSAssert( [NSThread isMainThread], #"called from non-main thread" );
// do UI updates here
}
As warrenm said you shouldn't update your UIView from a different thread than the Main thread (UI thread). Still, you asked if there is any workaround for what's going on. To be honest, you should try to, instead of blocking the access of the second thread to your method, understand why the methods is called twice. This is more a logical problem than anything else and you should try to fix that, instead of trying a shortcut.
I don't quite understand about how to deal with next kind of task:
#implementation SomeInterface
-(void)DoSomething
{
MyObj * mo = [MyObj new];
[mo doJob];
}
end
The question is - how can mo send message back to SomeInterface after doJob is finished?
Should I use NSNotificationCenter?
As of iOS 4, probably the easiest thing to do is to pass a block to doJob that dictates what it should do upon completion. So, for example...
MyObj.h:
// create a typedef for our type of completion handler, to make
// syntax cleaner elsewhere
typedef void (^MyObjDoJobCompletionHandler)(void);
#interface MyObj
- (void)doJobWithCompletionHandler:(MyObjDoJobCompletionHandler)completionHandler;
#end
MyObj.m:
- (void)doJobWithCompletionHandler:(MyObjDoJobCompletionHandler)completionHandler
{
/* do job here ... */
// we're done, so give the completion handler a shout.
// We call it exactly like a C function:
completionHandler();
/* alternatives would have been to let GCD dispatch it,
but that would lead to discussion of GCD and a bunch of
thread safety issues that aren't really relevant */
}
Within SomeInterface.m:
-(void)DoSomething
{
MyObj * mo = [MyObj new];
[mo doJobWithCompletionHandler:
^() // you can omit the brackets if there are no parameters, but
// that's a special case and I don't want to complicate things...
{
NSLog(#"whoop whoop, job was done");
// do a bunch more stuff here
}
];
}
I assume that in reality you're doing something that ends up being asynchronous in DoJob (or else you'd just wait until the method returns); in that case you might want to use GCD's dispatch_async with the result of dispatch_get_main_queue to ensure the completion handler takes place back on the main thread.
Joachim Bengtsson has written a good introductory guide to blocks. As to how they interact with Grand Central Dispatch (and how to use GCD in general), Apple's documentation is good.
yes, you can use NSNotificationCenter or write callback method
I must be doing something wrong, but the Automatic Reference Counting docs don't give me a hint on what it might be. What I'm doing is calling a method with a block callback from inside a delegate method. Accessing that same delegate from inside the block results in a bad access. The problem is the object I'm passing - loginController which is sending the message to its delegate - is clearly not released, when I don't access it inside the block I can call the method multiple times without an issue. Here's my code:
- (void)loginViewDidSubmit:(MyLoginViewController *)loginController
{
NSString *user = loginController.usernameLabel.text;
NSString *pass = loginController.passwordLabel.text;
__block MyLoginViewController *theController = loginController;
[self loginUser:user withPassword:pass callback:^(NSString *errorMessage) {
DLog(#"error: %#", errorMessage);
DLog(#"View Controller: %#", theController); // omit this: all good
theController = nil;
}];
}
NSZombieEnabled does not log anything and there is no usable stack trace from gdb. What am I doing wrong here? Thanks for any pointers!
Edit:
I figured the problem has a bigger scope - the callback above is called from an NSURLConnectionDelegate method (the block itself is a strong property for that delegate so ARC should call Block_copy()). Do I need to take special measurements in this scenario?
Flow (the loginController stays visible all the time):
loginController
[delegate loginViewDidSubmit:self];
View Delegate
(method shown above calls the loginUser: method, which does something like:)
httpDelegate.currentCallback = callback;
httpDelegate.currentConnection = // linebreak for readability
[[NSURLConnection alloc] initWithRequest:req
delegate:httpDelegate
startImmediately:YES];
NSURLConnectionDelegate
- (void)connection:(NSURLConnection *)aConnection
didFailWithError:(NSError *)error
{
if (NULL != currentCallback) {
currentCallback([error localizedDescription]);
self.currentCallback = NULL;
}
}
And this is where I get the bad access, but ONLY if I access that loginController variable...
Set copy attribute to the property, or just call 'copy' method for the block.
- (void)loginUser:(NSString *)user withPassword:(NSString *)pass callback:(void (^callback)(NSString *))
{
callback = [callback copy];
The actual solution was that I had the block as a strong property, but it should have been a copy property! D'oh!
First "Solution":
I just found a way to prevent the bad access. As shown in my Edit above, the View Delegate forwards the block to the httpDelegate (an instance of another class), which in turn keeps a strong reference to the block. Assigning the block to a temporary variable and forwarding the temporary block variable solves the problem, for whatever reason. So:
This crashes on block execution, as described
httpDelegate.currentCallback = callback;
This works
MyCallbackType aCallback = callback;
httpDelegate.currentCallback = aCallback;
I'll accept this as the answer, if anybody has more insights I'm happy to revise my decision. :)
I figure what is happening there is that the loginController is dead right after calling its delegate. Therefore a crash occurs. Without more information I can think of possible scenarios only:
The block do not retains the loginController object (__block type modifier). If the block is executed asynchronously, the loginController might no longer be available if it was killed elsewere. Therefore, no matter what you want to do with it, you wont be able to access it inside the block and the app will crash. This could happen if the controller is killed after sending loginViewDidSubmit.
I think most likely this could be your situation: The loginController calls its delegate object. The delegate method ends up synchronously invoking the callback block that kills the controller. The controller is expected to be alive after invoking the delegate method. Killing it inside the delegate method, most likely will cause crashes to happen. To make sure this is the problem, simply nil the loginController in the delegate method and put an NSLog statement in the controller after calling the delegate, never mind the block, you will get a crash there.
Perhaps if you paste some code we could help more.
My best.
I am writing a program that displays to a console-like UITextView different events generated by my AudioSession and AudioQueues. For instance, when I detect that my audio route has changed, I just want a quickie message displayed on the screen on my iPhone that this happened. Unfortunately, I believe I am getting into some race condition nastiness, and I'm not sure what the best solution to solve this is.
When I run my program, my debug console spits this out:
bool _WebTryThreadLock(bool), 0x1349a0: Tried to obtain the web lock from a thread other than the main thread or the web thread. This may be a result of calling to UIKit from a secondary thread. Crashing now...
This happens on a line of code:
textView.text = string;
I tried this:
[textView performSelectorOnMainThread:#selector(setText:) withObject:string waitForDone:YES];
And this seemed to have fixed it, but I'm pretty sure I shouldn't be doing something like this to get it to work. Unfortunately, this doesn't work with [UITextView scrollVisibleWithRange:] since this takes an NSRange, which isn't a descendant of NSObject. I think what I am doing is fundamentally wrong.
This code is called from an interruption listener, which runs from the audio queue's thread. Is there something that I should be doing that will make my updates to my textview blocking so I'm not getting this problem?
Thanks.
You are allowed to do anything about the view only from main thread, you did the right thing.
If it requires more parameters or primitive you may need a proxy function.
This is how I make a proxy function
// the caller should be like this
UTMainThreadOperationTextViewScroll *opr = [[UTMainThreadOperationTextViewScroll alloc] init];
opr.textView = textView;
opr.range = NSMakeRange(5, 10);
[UTMainThread performOperationInMainThread:opr];
[opr release];
// the Utility classes goes below
#interface UTMainThreadOperation : NSObject
- (void)executeOperation;
#end
#implementation UTMainThread
+ (void)performOperationInMainThread:(UTMainThreadOperation *)operaion
{
[operaion performSelectorOnMainThread:#selector(executeOperation) withObject:nil waitUntilDone:NO];
}
#end
#implementation UTMainThreadOperationTextViewScroll
#synthesize textView;
#synthesize range;
- (void)dealloc { /* I'm too lazy to post it here */ }
- (void)executeOperation
{
[textView scrollVisibleWithRange:range];
}
#end
PS. some declarations omitted