In my project, I have a function like this:
- (void)doSomething:(NSError**)error {
...
}
I need to call this function on another thread by using function performSelector:onThread:withObject:waitUntilDone: , something like this:
[self performSelector:#selector(doSomething:) onThread:anotherThread withObject:??? waitUntilDone:NO];
But the function parameter is of type NSError**. I am considering refactor the parameter type of function -(void)doSomething: from NSError** to NSValue* and pass NSValue* type as argument.
Which means, I need to wrap the &error (which is of type NSError **) into a NSValue and pass it as argument, and unwrap it later. How to wrap & unwrap NSError** with NSValue class?
I think you can use NSValue's valueWithPointer: and pointerValue. But I would suggest you use something else, like GCD to run a block asynchronously instead of changing your method's signature to fit the limitations of performSelector:
dispatch_async(anotherQueue, ^{
[self doSomething:&error];
});
Also this question has a few more ideas on how to approach this problem if you really want to go down that path.
You need to rethink your approach to this problem. Your method:
- (void)doSomething:(NSError**)error
follows the standard Objective-C pattern of passing the address of an NSError * variable so that the method can set the variable to return the error.
If you try to call this method asynchronously, whether with performSelector:onThread:withObject:waitUntilDone: as you are attempting or using GCD (as Felipe Cypriano has also suggested), you have to be careful - the variable whose address you pass must exist as the time the async call is executed, and even after you've addressed that you have to figure out when the async call has finished so you can check if it has set the variable...
A common way to deal with issues like this is to use a completion block which the async method calls when it is finished, passing on any results - an NSError * in your case. For example you could write a method:
- (void) doSomethingAsyncAndThen:(void (^)(NSError *))completionBlock
{
dispatch_async(dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0),
^{
NSError *error = nil;
[self doSomething:&error];
completionBlock(error);
});
}
and call it like:
[self doSomethingAsyncAndThen:^(NSError *error) { NSLog(#"error: %#", error); }];
though you will want to do something other than just NSLog the result.
HTH
Related
I'm attempting to get Background App Refresh going in my iOS application. However, I'm having some trouble understanding code blocks.
I've done some research on it, and would say I have a beginner's understanding so far. The method in question is:
-(void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler{
This method wants a UIBackgroundFetchResult return type. Due to the complexity of my application though, I cannot return that with ease. There's a lot that happens when pulling data from the internet in Background mode.
In the body of that method, I have a custom method that also has a completion block. What I'm trying to do is define another custom method in my code that would be assigned to the completion handler.
In my data manager, I have a property defined as :
#property (copy, nonatomic) void (^fetchCompleted)(UIBackgroundFetchResult);
In the performFetchWtihCompletionHandler method implementation, I call on my data manager:
-(void)fetchNewDataWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler{
_fetchCompleted = completionHandler;
_state = DMSMT_WaitingForPartnerData;
[self getActiveQueues];
}
Once my downloads are completed, I call on the fetchCompleted method:
[self fetchCompleted];
Herein lies my problem. I need to pass a UIBackgroundFetchResult argument, but I see no way to do that. I tried [self fetchCompleted:UIBackgroundFetchResultNewData]; but it yells at me.
Any ideas?
Thanks in advance.
EDIT:
Here was the fix. So simple!
if(_fetchCompleted != nil){
[self fetchCompleted](UIBackgroundFetchResultNewData);
}
You are treating fetchCompleted as a method but it is a block! Try this out:
-(void)getActiveQueues {
// Do some work here
// When you are finished...
// Set the appropriate result
UIBackgroundFetchResult result;
// Check to make sure fetchCompleted is not NULL
if (self.fetchCompleted) {
// Invoke the block!
self.fetchCompleted(result);
}
}
This method wants a UIBackgroundFetchResult return type
No, it wants a void return type. One of the parameters is of type UIBackgroundFetchResult. Parameters are not return results. UIBackgroundFetchResult is just a type of variable.
Which appears to flow into your error. [self fetchCompleted] is the getter that will return the fetchCompleted variable. It doesn't do anything with it.
To perform a block, use function-like syntax. E.g. [self fetchCompleted]().
I have been reading up on Objectice-C blocks as I have been running into them more and more lately. I have been able to solve most of my asynchronous block execution problems, however I have found one that I cannot seem to fix. I thought about making an __block BOOL for what to return, but I know that the return statement at the end of the method will be executed before the block is finished running. I also know that I cannot return a value inside the block.
- (BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender {
if ([identifier isEqualToString:#"Reminder Segue"]) {
eventStore = [[EKEventStore alloc] init];
[eventStore requestAccessToEntityType:EKEntityTypeReminder completion:^(BOOL granted, NSError *error) {
if (!granted) {
UIAlertView *remindersNotEnabledAlert;
remindersNotEnabledAlert = [[UIAlertView alloc] initWithTitle:#"Reminders Not Enabled" message:#"In order for the watering reminder feature to function, please allow reminders for the app under the Privacy menu in the Settings app." delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
//I would like to put a simple return NO statement here, but I know it is impossible
}
}];
}
return YES;
}
How do I create a simple return statement from a block?
While the immediate idea might be to make your asynchronous request synchronous, that's rarely a good idea, and do to so in the middle of a segue, such as this case, is likely to be problematic. It's almost never a good idea to try to make an asynchronous method synchronous.
And, as smyrgl points out, the idea of "can't I just return a value from the block" is intuitively attractive, but while you can define your own blocks that return values (as Duncan points out), you cannot change the behavior of requestAccessToEntityType such that it returns a value in that manner. It's inherent in its asynchronous pattern that you have to act upon the grant state within the block, not after the block.
So, instead, I would suggest a refactoring of this code. I would suggest that you remove the segue (which is likely being initiated from a control in the "from" scene) and not try to rely upon shouldPerformSegueWithIdentifier to determine whether the segue can be performed as a result of a call to this asynchronous method.
Instead, I would completely remove that existing segue and replace it with an IBAction method that programmatically initiates a segue based upon the result of requestAccessToEntityType. Thus:
Remove the segue from the button (or whatever) to the next scene and remove this shouldPerformSegueWithIdentifier method;
Create a new segue between the view controllers themselves (not from any control in the "from" scene, but rather between the view controllers themselves) and give this segue a storyboard ID (for example, see the screen snapshots here or here);
Connect the control to an IBAction method, in which you perform this requestAccessToEntityType, and if granted, you will then perform this segue, otherwise present the appropriate warning.
Thus, it might look something like:
- (IBAction)didTouchUpInsideButton:(id)sender
{
eventStore = [[EKEventStore alloc] init];
[eventStore requestAccessToEntityType:EKEntityTypeReminder completion:^(BOOL granted, NSError *error) {
// by the way, this completion block is not run on the main queue, so
// given that you want to do UI interaction, make sure to dispatch it
// to the main queue
dispatch_async(dispatch_get_main_queue(), ^{
if (granted) {
[self performSegueWithIdentifier:kSegueToNextScreenIdentifier sender:self];
} else {
UIAlertView *remindersNotEnabledAlert;
remindersNotEnabledAlert = [[UIAlertView alloc] initWithTitle:#"Reminders Not Enabled" message:#"In order for the watering reminder feature to function, please allow reminders for the app under the Privacy menu in the Settings app." delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[remindersNotEnabledAlert show];
}
});
}];
}
You CAN return a value from a block, just like from any function or method. However, returning a value from a completion block on an async method does not make sense. That's because the block doesn't get called until after the method finishes running at some later date, and by then, there is no place to return a result. The completion method gets called asynchronously.
In order to make a block return a value you need to define the block as a type that does return a value, just like you have to define a method that returns a value.
Blocks are a bit odd in that the return value is assumed to be void if it's not specified.
An example of a block that returns a value is the block used in the NSArray method indexOfObjectPassingTest. The signature of that block looks like this:
(BOOL (^)(id obj, NSUInteger idx, BOOL *stop))predicate
The block returns a BOOL. It takes an object, an integer, and a pointer to a BOOL as parameters. When you write a block of code using this method, your code gets called repeatedly for each object in the array, and when you find the object that matches whatever test you are doing, you return TRUE.
If you really want to make a block synchronous (although I question the validity of doing so) your best bet is to use a dispatch_semaphore. You can do it like this:
dispatch_semaphore_t mySemaphore = dispatch_semaphore_create(0);
__block BOOL success;
[eventStore requestAccessToEntityType:EKEntityTypeReminder completion:^(BOOL granted, NSError *error) {
success = granted;
dispatch_semaphore_signal(mySemaphore);
}];
dispatch_semaphore_wait(mySemaphore, DISPATCH_TIME_FOREVER);
However again I don't think you want to do this, especially in a segue as it will stall the UI. Your better bet is to rearchitect what you are doing so that you don't have a dependency on the async process being completed in order to continue.
I'm new to objective -c & still trying to wrap my head around this. Say I have something like this:
- (void)viewDidLoad
{
[super viewDidLoad];
[[CustomAPIClient sharedClient] getPath:#"/api/feed" parameters:[NSDictionary dictionary] success:^(AFHTTPRequestOperation *operation, id responseObject) {
}];
**//Now, how do I get my responseObject out here??**
}
I'm probably going about this the wrong way, but basically, I have a controller that will display a certain view. I will also have another controller like this:
- (void)viewDidLoad
{
[super viewDidLoad];
[[CustomAPIClient sharedClient] getPath:#"/api/profile" parameters:[NSDictionary dictionary] success:^(AFHTTPRequestOperation *operation, id responseObject) {
}];
**//Now, how do I get my responseObject out here??**
}
So my questions are:
How can I get the responseObject outside of that block?
Is there a way for me to stay authenticated without calling the [[CustomAPIClient... block?
Thanks in advance guys.
To better explain what I'm doing. I have table view cells:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [self.meResponseObjectFromAboveCode count];
}
So I want to have whats in my response object, show up in my table view
Blocks are units of execution. In this case, you are giving this method a block that it can execute once the request returns successfully. So, first of all, you're giving the networking client some code and saying "I want it executed whenever you're done, and also right now." If, in theory, it was possible to have the responseObject "out here", it would be nil (or at least not what you want) because after the getPath method is executed, it stores the block to be executed later (much later) and then continues on to executing the code right after that method, essentially immediately after you issue the network request, giving it no time to actually make the request, return and make responseObject available.
Second, blocks are closures. Meaning, it captures the variables outside it's own scope, but anything passed into it or initialized within it is "enclosed" within it and is invisible outside of it.
Now, to make this point very clear, you could get around this by declaring a variable before calling getPath like __block id veryBadIdea; and then (because of the __block modifier) you can change it inside of the block. So, in this bad idea example, you would do veryBadIdea = responseObject and then after getPath you could do NSLog(#"%#", veryBadIdea); and see that it is nil. Why? Because by the time NSLog is called, the network request likely has not even been sent. By the time veryBadIdea is assigned the value of responseObject the viewDidLoad has long since finished and returned.
So, use responseObject within the block, cause thats where it's available and that's where (and when) you want any code using it to run.
I have written a recursive block following these guidelines:
NSMutableArray *groups = [NSMutableArray arrayWithArray:#[#"group1", #"group2", #"group3", #"group4"];
__block CommunicationCompletionHandler completion = [^{
[groups removeObjectAtIndex:0];
if ([groups count] > 0) {
// This will send some information to the network, and calls the completion handler when it receives a response
[mySocket saveGroup:groups[0] completion:completion];
}
} copy]; // Removing copy here doesn't work either
[mySocket saveGroup:groups[0] completion:completion];
In the saveGroup:completion: method, I add the completion handler to an array:
self.completionHandlers[SaveGroupCompletionHandlerKey] = [completion copy];
And when I receive a response, I call the following method (key is in this case SaveGroupCompletionHandlerKey):
- (void)performCompletionHandlerForKey:(NSString *)key {
if (self.completionHandlers[key]) {
((CommunicationCompletionHandler)self.completionHandlers[key])();
[self.completionHandlers removeObjectForKey:key];
}
}
The problem is that the completion handler only gets called once. The removeObjectForKey: line makes the block deallocate. If I uncomment that line, everything works fine. I'm not sure how the array has the last reference to this block, since I add a copy (which I believe is being optimized to a retain).
For clarity, the flow of the app is:
Send data for first group over network
Receive response
Call completion handler
In the completion handler, send data for next group (this is the recursive part).
Anybody here who can point out what I'm doing wrong?
In -performCompletionHandlerForKey: you remove the completion handler from your dictionary after executing the block, which means that the handler will always be removed from the dictionary after one run.
Instead, store the block in a temporary variable and remove it from the dictionary before executing the block.
By the way, the advice to remove the weak reference is wrong. As your code is written now, your block will never be deallocated. The typical block recursion pattern is this:
__weak __block MyBlock weakHandler;
MyBlock handler = ^ {
if (foo) {
MyBlock strongHandler = weakHandler;
[bar asyncOperationWithCompletion:strongHandler];
}
};
weakHandler = handler;
[bar asyncOperationWithCompletion:handler];
A popular way to avoid retain retain cycles is to create a weak reference to the object before defining the block, then create a strong reference inside the block and set it to that weak reference. This method is frequently used to avoid strongly capturing self inside of blocks:
- (void)someMethod {
__weak MyType *weakSelf = self;
[self someMethodWithABlockArg:^{
MyType *strongSelf = weakSelf;
[strongSelf someOtherMethod];
}];
}
The strong reference created inside the block prevents the object from being deallocated while the block is running. You can, of course, do the same with any object type.
Edit2: Looks like [someBlock copy] is indeed fine. Have you tried running Analyze on the code? It may be that completion is not yet initialized when it is referred to inside of the block.
I have a AuthService class that has a method to perform asynchronous connection to login. This class has implemented NSURLConnectionDataDelegate protocol so that when the server responses, it calls the completion handler previously set by a View Controller to update UI.
This is the definition of that completion handler
#property void (^completionHandler)(LoginResult *result);
This is when the class receives server response
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
NSString *response = [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease];
//Do something with the response and create an instance of LoginResult class
self.completionHandler(loginResult);
}
If the completion handler block merely just calls NSLog to write to console the information of the login result which is passed as argument, then it runs perfectly with no error. But when I want to call methods of the ViewController that owns the block, something strange just happens.
I know that there is a retain cycle when you include an object in a block which owns that block. So this is the way how I code it.
__block typeof(self) bself = self;
[authService login:blablabla completionHandler:^(LoginResult *result) {
[bself didReceiveLoginResult:result];
}
I assumed this will prevent from running into a retain cycle. But I got "Thread: EXC_BAD_ACESS" error when debugging.
P.S.
Following codes for example run perfectly even if that property is not declared as "copy"
[authService login:blablabla completionHandler:^(LoginResult *result) {
NSLog(#"Login %#", result.success ? #"success" : #"failed");
}
The property should be declared as copy otherwise the block will stay on stack and can be already deallocated when you call it.
Also, there are simple ways how to prevent a retain circle. Just release the block when you have used it, e.g.
self.completionHandler(loginResult);
self.completionHandler = nil;
No clever magic with __block is neccessary. Retain cycles are allowed when they are temporary.
Edit:
If there is no reference to self in the block, the compiler will make it a global block and it won't ever get deallocated. See http://www.cocoawithlove.com/2009/10/how-blocks-are-implemented-and.html
Blocks need to be copied if you want to use them outside of the current function, so you will need to copy it before storing it in your property:
- (void)setCompletionHandler:(void (^)(LoginResult *))handler {
_completionHandler = [handler copy];
}
Then when you assign the completion handler in your login:completionHandler: method, it will be copied before being stored in the instance variable.
In this way, the block you pass to the function will be copied before being stored in the property, and the copy will be located on the heap, not the stack, so it'll still exist when you run it later.