How to call performSelectorInBackground with a function having arguments?
Asked Answered
S

5

8

Sorry for the newbie question (maybe). I'm developing an app for ios and i'm trying to execute an external xml reading out of the main thread in order not to freeze the ui while the call is doing its magic.

This is the only way i know to made a process not to execute in the main thread in objective c

[self performSelectorInBackground:@selector(callXml)
                           withObject:self];

so i did incapsulate my call into a function

 - (void)callXml{
     [RXMLElement elementFromURL:[NSURL URLWithString:indXML]];
 }

Now i have to make the string indXML be a parameter of the function in order to call different xml as i need to. Something like

 - (void)callXml:(NSString *) name{
     [RXMLElement elementFromURL:[NSURL URLWithString:indXML]];
 }

In this case, how the call to performSelector change? If i do it in the usual way i get syntax errors:

[self performSelectorInBackground:@selector(callXml:@"test")
                           withObject:self];
Soemba answered 14/5, 2013 at 8:46 Comment(0)
I
16
[self performSelectorInBackground:@selector(callXml:)
                       withObject:@"test"];

ie: what you pass in as withObject: becomes the parameter to your method.

Just as a point of interest here's how you could do it using GCD:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    [self callXml:@"test"];

    // If you then need to execute something making sure it's on the main thread (updating the UI for example)
    dispatch_async(dispatch_get_main_queue(), ^{
        [self updateGUI];
    });
});
Improvvisatore answered 14/5, 2013 at 8:48 Comment(4)
In the second way the code will be execute out of the main thread without messing up with the ui flow?Soemba
Yes. See here ... "Concurrent queues (also known as a type of global dispatch queue) execute one or more tasks concurrently, but tasks are still started in the order in which they were added to the queue. The currently executing tasks run on distinct threads that are managed by the dispatch queue. The exact number of tasks executing at any given point is variable and depends on system conditions."Improvvisatore
I was still guessing what to do. I have still problem using the performSelector for the way you suggested to use it throws me an exception, but it's true i've solved the problem abandoning performSelector and using dispatch_async. Deal! ;)Soemba
Just picking up on your problem trying the performSelector way: In the 'NSInvalidArgumentException' +[funzioni callXml:]) note the '+'. It means it's looking for a class method ( +(void)callXml: ) not an instance method ( -(void)callXml: ) implying that you were calling performSelector from within a class method rather than an instance method...Improvvisatore
M
8

For this

- (void)callXml:(NSString *) name{
     [RXMLElement elementFromURL:[NSURL URLWithString:indXML]];
 } 

You can call like this

  [self performSelectorInBackground:@selector(callXml:)
                               withObject:@"test"];

If your method has parameter then : is used with method_name and pass parameter as withObject argument

Magallanes answered 14/5, 2013 at 8:48 Comment(4)
It throws an error: Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[NSThread initWithTarget:selector:object:]: target does not implement selector (*** +[funzioni callXml:])' ----- where "funzioni" is my class. I tried to define the function in my .m and in my .h. No fortune.Soemba
@ElisabettaFalivene Mike Pollard already shown you how to update UI from background thread.Price
using GCD? Yes, that's why i choose his answer. The problem is solved for me, but i was curios of solving the issue i had with performSelector. ^^' It's not so important, though, so i give up.Soemba
@ElisabettaFalivene: Bhargavi gave the correct answer based on all the information you gave. Your error is inconsistent with your question. Your error (that it is trying to call a class mehtod, i.e. the object you are calling it on, self is a class) indicates that you are making the call from a class method. If that were the case, then what you were doing previously (if called from the same place) when calling callXml was also calling a class method. However, in your question, you showed callXml and callXml: as instance methods.Dansby
P
1

The selector you use needs to be an exact match to the method name - the method name includes the colon (:) characters. So your selector should be:

@selector(callXml:)

If you want to pass a parameter.

If you need to pass more complex data, you have 3 options:

  1. Pass an NSArray or NSDictionary containing the parameters
  2. Pass a custom object which contains all of the required data as attributes
  3. Use GCD (GCD_libdispatch reference)
Pilothouse answered 14/5, 2013 at 8:52 Comment(2)
Passing an NSString is fine... see the other answersImprovvisatore
3. Option would be passing a custom object, which contains all needed params.Humpbacked
C
1

Hi try this,

 RXMLElement *rootXML= [self performSelectorInBackground:@selector(callXml:)
                               withObject:@"test"];

- (RXMLElement *)callXml:(NSString *) name{

         NSLog(@"%@",name);//Here passed value will be printed i.e test

     return [RXMLElement elementFromURL:[NSURL URLWithString:indXML]];
 } 
Crosstree answered 14/5, 2013 at 8:56 Comment(10)
cool! and what if the function return something i need to get? (i.e. return [RXMLElement etc.....])Soemba
if u want to get from method that called method should return something i.e instead of void use your return type.ex - (return type)callXml:(NSString *) name { } k @ElisabettaFaliveneCrosstree
if u want to store it in some variable then type var_name=[self method];then the called method will return some value. @ElisabettaFaliveneCrosstree
It's what i was guessing but when i did - (RXMLElement *)callXml:(NSString *) xmlName{ return [RXMLElement elementFromURL:[NSURL URLWithString:xmlName]]; } and RXMLElement *rootXML=nil; rootXML = [self performSelectorInBackground:@selector(callXml) withObject:indXMLdateAggiornamento]; it began saying "Assigning to 'RXMLElement *' from incompatible type 'void'"Soemba
can u tell me what typ will be return for this line [RXMLElement elementFromURL:[NSURL URLWithString:indXML]];? it will be of type (RXMLElement *)???Crosstree
hey just use return [RXMLElement elementFromURL:[NSURL URLWithString:indXML]]; in your codeCrosstree
RXMLElement *rootXML=[self performSelectorInBackground:@selector(callXml:) withObject:@"test"];Crosstree
hi friend check my updated answer and let me know @ElisabettaFaliveneCrosstree
let us continue this discussion in chatCrosstree
just try this RXMLElement* var ; - (void)callXml:(NSString *) name{ var= [RXMLElement elementFromURL:[NSURL URLWithString:indXML]]; NSLog("%@",var); } and post what u get in consol @ElisabettaFaliveneCrosstree
O
0

Since performSelectorInBackground:withObject: takes only one object argument. One way to get around this limitation is to pass a dictionary (or array) of arguments to a "wrapper" method that deconstructs the arguments and calls your actual method.

Obtain answered 14/5, 2013 at 8:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.