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];
}
}
Related
DISCLAIMER: This is a long post, but could prove very valuable for those grappling with using the new ObjectiveC JavascriptCore framework and doing asynchronous coding between ObjC and JS.
Hi there, I'm super new to Objective C and am integrating a javascript communication library into my iOS app.
Anyway, I've been trying my hand at using the new ObjectiveC JavaScriptCore Framework introduced in iOS7. It's pretty awesome for the most part, though quite poorly documented so far.
It's really strange mixing language conventions, but also kind of liberating in some ways.
I should add that I am of course using ARC, so that helps a lot coming from the Javascript world. But I have a question that's pretty specific around memory use issues when moving between ObjectiveC and the JSContext callBacks. Like if I execute a function in Javascript that then does some asynchronous code, and then calls back to a defined ObjectiveC block, and then that calls a defined JS callback... I just want to make sure I'm doing it right (ie. not leaking memory some place)!
Just to do things proper (because I reference a the class self to call the ObjectiveC callBacks I create a weakSelf so it plays nice with ARC (referenced from question: capturing self strongly in this block is likely to lead to a retain cycle):
__unsafe_unretained typeof(self) weakSelf = self;
Now, say I have a JSContext and add a function to it. I want this function to take a callBack function and call it with "Hello" as an argument as well as pass ANOTHER function as a callBack. ie.
// Add a new JSContext.
JSContext context = [[JSContext alloc] initWithVirtualMachine:[[JSVirtualMachine alloc] init]];
// Add a function to the context. This function takes a callBack function and calls it back with "Hello"
[context evaluateScript: #"var functionA = function(callBack){
var aMessage = "Foo";
callBack(aMessage, function(message){
/* message should say: Foo Bar */
});
}" ];
// Note, if you try to copy this code, you will have to get rid of the returns in the JS script.
Okay, so we have our basic JS side of things. Now to add the ObjectiveC complexity. I'm going to add the first ObjectiveC CallBack block:
context[#"functionB"] = ^(NSString *theMessage, JSValue *theCallBack){
[weakSelf objCFunction:theMessage withCallBack:theCallBack];
};
In the same class all this is happening in I also have the method definition. This is the place that causes the most concern to me:
-(void)objCFunction:(NSString *)message withCallBack:(JSValue *)callBack
{
NSString *concatenatedString = [NSString stringWithFormat:#"%#%#", message, #"Bar"];
[callBack callWithArguments:#[concatenatedString]];
}
So when I call:
[context evaluateScript: #"functionA(functionB);" ];
It should pass through the chain, and it does exactly what I expect it to do.
My main concern is that I hope I'm not somehow capturing a JSValue somewhere along this chain that is then leaking out.
Any help in helping me understand how ARC/the JSMachine would manage this approach to calling callBacks fluidly between Objective C and Javascript, would be super valuable!
Also, I hope this question helps others out there who are experimenting with this framework.
Thanks!
The problem with retain cycles occurs when you have two objects, each of which retains part of another. It's not specific to JavascriptCore. It's not even specific to blocks although blocks make the problem much easier to blunder into.
E.g.
#interface ObjcClass : NSObject
#property (strong,nonatomic) JSValue *badProp;
- (void) makeEvilRetainWithContext:(JSContext *) context;
#end
- (void) makeEvilRetainWithContext:(JSContext *) context{
context[#"aFunc"]=^(JSValue *jsValue){
self.badProp=jsValue;
};
}
The self.context[#"aFunc"] now retains the ObjcClass object because self.badProp is now inside the function obj inside the context created by assigning the block to #"aFunc". Likewise, the context is retained because one of its own strongly retained values is retained in self.badProp.
Really, the best way to avoid all this is just to not try and store JSValue in objective-c objects ever. There really doesn't seem to be a need to do so e.g.
#property (strong,nonatomic) NSString *goodProp;
- (void) makeGoodFunc:(JSContext *) context;
#end
- (void) makeGoodFunc:(JSContext *) context{
context[#"aFunc"]=^(JSValue *jsValue){
self.goodProp=[JSValue toString];
};
}
You code isn't a problem because simply passing a JSValue (even a function) through a method won't retain it.
Another way to think of it might be: After, objCFunction:withCallBack: executes, would there be anyway for the object represented by self to access the JSValue passed as callBack? If not, then no retain cycle.
Check out the WWDC introduction "Integrating JavaScript into Native Apps" session on Apple's developer network: https://developer.apple.com/wwdc/videos/?id=615 - it contains a section on Blocks and avoiding capturing JSValue and JSContext
In your sample code above, all the JSValues are passed as arguments (the way Apple recommends) so the references only exist whilst the code is executed (no JSValue objects are captured).
Got a singleton class, so called RequestManager, which shall handle requests made by different modules and background tasks of my application.
#interface RequestFactory : NSObject
- (void)requestDataWith:(NSString *)token
id:(NSString *)id
sender:(id<RequestFactoryDelegate>)sender;
...
#end
Then I got another class, so called SessionDelegate, which shall handle all the callbacks during the request.
#interface SessionDelegate : NSObject <NSURLSessionDelegate, NSURLSessionTaskDelegate, NSURLSessionDataDelegate>
#property (weak, nonatomic) id <RequestFactoryDelegate> delegate;
#end
My idea is to encapsulate the functions in these classes to not overload my classes, because I need a lot of helper classes with CommonCrypto and so on.
So I set quickly coded a protocol RequestFactoryDelegate to send the received data to the sender who initiated the origin request.
- (void)requestDataWith:(NSString *)token
id:(NSString *)id
sender:(id<RequestFactoryDelegate>)sender
{
self.sessionDelegate.delegate = sender;
NSMutableURLRequest *request = //create the request here
NSURLSessionDataTask *dataTask = [self.defaultSession dataTaskWithRequest:request];
[dataTask resume];
}
Well, it works if I have an object, let us call it senderA which sends the requests, because the set delegate is always senderA itself.
The problem occurs having another object, e.g. senderB which sends requests - not even at the same time - but very shortly after senderA send.
- (void)foo
{
[requestFactory requestDataWith:token
id:id
sender:senderA]; // let's assume this takes 20s
[requestFactory requestDataWith:token
id:id
sender:senderB]; // let's assume this takes 1s
}
Because the request of senderA is still in progress, senderB sets the delegate to him and what happens is the delegate function of senderB is run twice.
<senderB>
<senderB>
Well... I really need to implement an own custom delegate (whether or not in the same class as the RequestFactory or not), but how to I handle the callback methods so I can respond properly to either senderA or senderB?
My last idea is to override the NSURLSessionTasks class and implement an own delegate property or block property or whatever.
Many thanks in advance.
You can attach an arbitrary object to a task:
NSMutableURLRequest* req = [NSMutableURLRequest requestWithURL:url];
id whatever = // anything at all!
[NSURLProtocol setProperty:whatever forKey:#"thing" inRequest:req];
NSURLSessionDownloadTask* task = [[self session] dataTaskWithRequest:req];
And retrieve it later (in the delegate) like this:
NSURLRequest* req = task.originalRequest;
id thing = [NSURLProtocol propertyForKey:#"thing" inRequest:req];
The value here (whatever) can be any object. It can be a completion handler callback - I've done this and it works just fine.
In Objective-C, there is an alternative to subclassing that might be what you want here: associating objects.
It works like this: you can "attach" (associate) an object to another object with a custom key and later retrieve it. So in your case, you would do something like:
#include <objc/runtime.h>
// Top level of your .m file. The type and content of this
// variable don't matter much, we need the _address_ of it.
// See the first link of this answer for details.
static char kDelegateKey = 'd';
- (void)requestDataWith:(NSString *)token
id:(NSString *)id
sender:(id<RequestFactoryDelegate>)sender
{
NSMutableURLRequest *request = //create the request here
NSURLSessionDataTask *dataTask = [self.defaultSession dataTaskWithRequest:request];
// Associate the sender with the dataTask. We use "assign" here
// to avoid retain cycles as per the delegate pattern in Obj-C.
objc_setAssociatedObject(dataTask, &kDelegateKey, sender, OBJC_ASSOCIATION_ASSIGN);
[dataTask resume];
}
- (void)someOtherMethodWithDataTask:(NSURLSessionDataTask *)dataTask
{
// Read the attached delegate.
id<RequestFactoryDelegate> delegate = objc_getAssociatedObject(dataTask, &kDelegateKey);
// Do something with the delegate.
}
Here is my solution.
I just just use the unique identifier of each sessionTask object. So my delegate object contains a dictionary with blocks as values to execute on success/failure and the identifier as keys to identify the correct execution block.
In the .h file I declared the dictionary and a method to add a key/value object:
#property (nonatomic, strong) NSDictionary *completionHandlerDictionary;
- (void)addCompletionHandler:(CompletionHandlerType)handler
forTask:(NSString *)identifier;
And in the .m file I call the handler block.
- (void)addCompletionHandler:(CompletionHandlerType)handler
forTask:(NSString*)identifier
{
if ([self.completionHandlerDictionary objectForKey:identifier]) {
NSLog(#"Error: Got multiple handlers for a single task identifier. This should not happen.\n");
}
[self.completionHandlerDictionary setObject:handler forKey:identifier];
}
- (void)callCompletionHandlerForTask:(NSString *)identifier
{
CompletionHandlerType handler = [self.completionHandlerDictionary objectForKey:identifier];
if (handler) {
[self.completionHandlerDictionary removeObjectForKey: identifier];
NSLog(#"Calling completion handler.\n");
handler();
}
}
That's it, simple as it is.
I have an iPad app that uses a proprietary library object which registers for a "UIScreenDidConnectNotification". Occasionally this object is deallocated and reallocated behind the scenes. As it is in a library, I cannot ensure that it is properly removing this observer.
Is there a way for me to manually remove all/any observers for a specific notification (i.e. UIScreenDidConnectNotification) without having any access to the object that has registered. This would keep the application from sending the message to a deallocated object.
Update: Here is the easiest way to fix my problem. I wish I could do a better job, but life is too short.
#import
#import
#interface NSNotificationCenter (AllObservers)
#end
#implementation NSNotificationCenter (AllObservers)
// This function runs before main to swap in our special version of addObserver
+ (void) load
{
Method original, swizzled;
original = class_getInstanceMethod(self, #selector(addObserver:selector:name:object:));
swizzled = class_getInstanceMethod(self, #selector(swizzled_addObserver:selector:name:object:));
method_exchangeImplementations(original, swizzled);
// This function runs before main to swap in our special version of addObserver
+ (void) load
{
Method original, swizzled;
original = class_getInstanceMethod(self, #selector(addObserver:selector:name:object:));
swizzled = class_getInstanceMethod(self, #selector(swizzled_addObserver:selector:name:object:));
method_exchangeImplementations(original, swizzled);
}
/*
Use this function to remove any unwieldy behavior for adding observers
*/
- (void) swizzled_addObserver:(id)notificationObserver selector:(SEL)notificationSelector name:(NSString *)notificationName object:(id)notificationSender
{
NSString *notification = [[NSString alloc] initWithUTF8String: "UIScreenDidConnectNotification" ];
// It's a hack, but I just won't allow my app to add this type of notificiation
if([notificationName isEqualToString: notification])
{
printf("### screen notifcation added for an observer: %s\n", [notificationSender UTF8String] );
}
else
{
// Calls the original addObserver function
[self swizzled_addObserver:notificationObserver selector:notificationSelector name:notificationName object:notificationSender];
}
}
As it is in a library, I cannot ensure that it is properly removing this observer.
If the object is created in a library, it's not your responsibility to remove the object. If the library is deallocating the object without removing it from the notification center, that's a clear bug in the library.
Is there a way for me to manually remove all/any observers for a specific notification... without having any access to the object that has registered.
There's nothing in the API for NSNotificationCenter that lets you do that. Just the opposite, in fact -- the methods that let you remove the observer all require a pointer to a specific object.
I agree with both of Caleb's points: it is not your responsibility to perform this task and there is nothing in the API to support it.
However... if you feel like hacking something in to perform this task for whatever reason, refer to this thread: How to retrieve all NSNotificationCenter observers?
The selected answer of that thread has a category for NSNotificationCenter that allows you to retrieve all observers for a given notification name. Again, this is not recommended though.
Is there any way to ensure that a class posts a particular NSNotification?
(I have a set of classes, and I would like to enforce at compile-time (if possible) that the class posts a required NSNotification).
Alternatively, if that is not possible, is there any workaround?
It's fundamentally impossible to predict at compile time what will happen at run time. The closest you can get is static analysis, but even that can't predict anything that happens outside of your own code, such as inside Foundation.
You can, however, do this with unit tests, since the test runner actually runs the code under test.
You'll need to create a test bundle target, if you haven't already. Your target will use SenTestingKit to run your tests, which you create. (On the iPhone, you'll also need Google Toolbox for, uh, Mac. They have a handy tutorial on using GTM for iPhone tests.)
You'll create a SenTestCase subclass to test whether your real object posts a notification. It'll look something like this:
#interface FrobnitzerNotificationsTest: SenTestCase
{
BOOL frobnitzerDidCalibrate;
}
- (void) frobnitzerDidCalibrate:(NSNotification *)notification;
#end
#implementation FrobnitzerNotificationsTest
- (void) testFrobnitzerCalibratePostsNotification {
Frobnitzer *frobnitzer = …;
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
[nc addObserver:self
selector:#selector(frobnitzerDidCalibrate:)
name:FrobnitzerDidCalibrate
object:frobnitzer];
frobnitzerDidCalibrate = NO;
//This should post a notification named FrobnitzerDidCalibrate with the receiver as the object.
[frobnitzer calibrate];
//If it did, our notification handler set frobnitzerDidCalibrate to YES (see below).
[nc removeObserver:self
name:FrobnitzerDidCalibrate
object:frobnitzer];
STAssertTrue(frobnitzerDidCalibrate, #"Frobnitzer did not post a notification when we told it to calibrate");
}
- (void) frobnitzerDidCalibrate:(NSNotification *)notification {
frobnitzerDidCalibrate = YES;
}
#end
You'll need one instance variable and one notification-handler method for every notification you want to test for, and one test method for every method you want to test for notifications.
Also, if using GTM, you must substitute GTMSenTestCase for SenTestCase above.
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