0

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?

Leem.fin
  • 40,781
  • 83
  • 202
  • 354
  • 1
    Why do you need to wrap the `NSError` in an `NSValue`? – rmaddy Aug 12 '16 at 20:59
  • I want to wrap the `NSError**` and pass the wrapped NSValue as the argment , then call performSelector:withObject , – Leem.fin Aug 12 '16 at 21:06
  • 1
    1) Why do you need to wrap the `NSError` with `NSValue` just to pass it to `performSelector:withObject:`? There's no need to wrap it. 2) Why do you need to use `performSelector:withObject:`? There's always a better way than that. I suggest updating your question with more specific details about what you really need to accomplish so people can offer better advice. – rmaddy Aug 12 '16 at 21:08
  • I am going to update my question with more detail, thanks. – Leem.fin Aug 12 '16 at 21:09
  • updated with more detail – Leem.fin Aug 12 '16 at 21:17
  • There is no reason to wrap the NSError in NSValue. Just pass the NSError to the `withObject:` part of the call. – rmaddy Aug 12 '16 at 22:05
  • Do you control the implementation of `doSomething`? Because it's not going to be happy with wrapped NSError** even if you could wrap that. Also, the normal pattern of sending the address of an NSError* on the stack isn't going to work on an async call. The caller will need to allocate and retain an NSError*, and be sure to outlive the completion of doSomething. – danh Aug 12 '16 at 22:17
  • @rmaddy - you cannot pass the address of a variable to something expecting an object reference under ARC. – CRD Aug 12 '16 at 22:37
  • @CRD Right. Just do `NSError *localError = *error;`. Then pass `localError`. – rmaddy Aug 12 '16 at 22:38
  • @rmaddy - its an *out* parameter... – CRD Aug 12 '16 at 22:40
  • @CRD I overlooked that detail since that wasn't in the original question. – rmaddy Aug 12 '16 at 22:42

2 Answers2

0

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.

Community
  • 1
  • 1
Felipe Cypriano
  • 2,727
  • 22
  • 32
  • Thanks, but I need to use the `otherThread` variable, so, I need to use performSelector:onThread:WithObject – Leem.fin Aug 13 '16 at 05:37
0

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

CRD
  • 52,522
  • 5
  • 70
  • 86