How to I pass #selector as a parameter? - iphone

For the method:
[NSThread detachNewThreadSelector:#selector(method:) toTarget:self withObject:(id)SELECTOR];
How do I pass in a #selector? I tried casting it to (id) to make it compile, but it crashes in runtime.
More specifically, I have a method like this:
+(void)method1:(SEL)selector{
[NSThread detachNewThreadSelector:#selector(method2:) toTarget:self withObject:selector];
}
It crashes. How do I pass in the selector without crashing, so that the new thread can call the selector when the thread is ready?

The problem here isn't passing a selector to a method, per se, but passing a selector where an object is expected. To pass a non-object value as an object, you can use NSValue. In this case, you'll need to create a method that accepts an NSValue and retrieves the appropriate selector. Here's an example implementation:
#implementation Thing
- (void)method:(SEL)selector {
// Do something
}
- (void)methodWithSelectorValue:(NSValue *)value {
SEL selector;
// Guard against buffer overflow
if (strcmp([value objCType], #encode(SEL)) == 0) {
[value getValue:&selector];
[self method:selector];
}
}
- (void)otherMethodShownInYourExample {
SEL selector = #selector(something);
NSValue *selectorAsValue = [NSValue valueWithBytes:&selector objCType:#encode(SEL)];
[NSThread detachNewThreadSelector:#selector(methodWithSelectorValue:) toTarget:self withObject:selectorAsValue];
}
#end

You can convert between selectors and string objects using the NSStringFromSelector() and NSSelectorFromString() functions. So you can just pass string objects instead.
Alternately, if you don't want to change your methods, you can create an NSInvocation to create an invocation for your method call (since it can set up invocations with non-object arguments), and then to call it do [NSThread detachNewThreadSelector:#selector(invoke) toTarget:myInvocation withObject:nil];

Use NSValue, like so:
+(void)method1:(SEL)selector {
NSValue *selectorValue = [NSValue value:&selector withObjCType:#encode(SEL)];
[NSThread detachNewThreadSelector:#selector(method2:)
toTarget:self
withObject:selectorValue];
}
NSValue is intended as an object-wrapper for arbitrary non-object types.

Please see: passing a method as an argument

If you don't want to specify an object just use nil.
[NSThread detachNewThreadSelector:#selector(method:) toTarget:self withObject:nil];
If you need to pass an object to the selector it would look something like this.
Here I'm passing a string to the method "setText".
NSString *string = #"hello world!";
[NSThread detachNewThreadSelector:#selector(setText:) toTarget:self withObject:string];
-(void)setText:(NSString *)string {
[UITextField setText:string];
}

Related

How can I use [self performSelector: withObject: afterDelay:] method in +(void)classMethod

How can I use [self performSelector: withObject: afterDelay:] method in +(void)classMethod?
I've got a sprite defined in the following:
+(void)classMethod
{
CCSprite * sprite = [CCSprite spriteWithFile:#"sprite.png"];
//and hope afterDelay3.0second remove this sprite
[self performSelector:#select(clean:) withObject:sprite afterDelay:3.0];
}
+(void)clean:(CCSprite *)sprite
{
[sprite removeFromSuperView];
}
Technically self refers to the Object of given class, In your case you are not creating an Object so you won't be able to call Object methods from static method.
Visual
A quick test shows that, If you method is declared instance level then you will not be able to access it from your class method.
In your case,
+(void)classMethod
{
}
is a class method but,
[self performSelector:#selector(clean:) withObject:sprite afterDelay:3.0];
is an instance method of NSObject, That is why you are not able to call from your class method.
Apple Doc,
- (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay
That means you are accessing instance method from class method, which is not allowed.
I don't think you can use this Instance method in another Class method.
If you want to do some delay action, try NSTimer timerWithTimeInterval:target:selector:userInfo:repeats: instead.

BAD ACCESS exception performSelector:WithObject: method

I'm getting a EXC_BAD_ACCESS exception when I call performSelector:withObject: from a object that does implement the method I'm trying to call. Here's my code
SEL newSelector = NSSelectorFromString(#"mySelector:withCustomObject:");
[self performSelector:newSelector withObject:myCustomObject];
This causes a crash. However when I do this
[self performSelector:#selector(mySelector:withCustomObject:) withObject:myCustomObject];
it works.
Any ideas on why this is happening?
PS: none of the parameters are nil.
MORE CODE:
// My code to call this method
SEL newSelector = NSSelectorFromString(#"mySelector:withCustomObject:");
[self performSelector:newSelector withObject:self withObject:myCustomObject];
// this code is NOT called.
- (void) mySelector:(jObject *)sender withCustomObject:(jEvent *)customObject
{
NSDictionary *handlerData = [aProperty objectAtIndex:[event positionInMethodStack]];
NSString *newTitle = [handlerData objectForKey:#"newTitle"];
}
"mySelector:withCustomObject:" is the signature of a method with 2 arguments, such as
- (void)mySelector:(id)firstArgument withCustomArgument:(id)secondArgument { ... }
But you call performSelector:withObject:, which sends a message with only one argument to mySelector. The second argument is undefined, which probably causes the crash.
So if mySelector actually has 2 arguments, use performSelector:withObject:withObject:, otherwise fix the signature of the selector.

How to call a method from another method with arguments

I want to call another method from the updateButtonPressed method.
This is what I tried:
-(IBAction) updateButtonPressed{
[self loadScrollViewWithPage];
}
But the problem is that the loadScrollViewWithPage method has arguments. That method is like this:
- (void)loadScrollViewWithPage:(int)page {
}
How can I call this method?
If I understand correctly, you are wondering how to pass arguments along with messages to objects, is that right? Try:
-(IBAction) updateButtonPressed{
int foo = 4;
[self loadScrollViewWithPage:foo]; // a colon, followed by the argument
}
I suggest you read up on the Objective-C language in general, though.
http://developer.apple.com/library/mac/#documentation/cocoa/conceptual/objectivec/introduction/introobjectivec.html
- (IBAction) updateButtonPressed{
int tempValue=5;
[self loadScrollViewWithPage:tempValue];
}

Delegate callback from other NSThread

I have object with .delegate property which i manipulate in method 'doJob'. I assign this property with 'self' and my function is being called when this object finishes his job. Till now everything is fine.
Now i want to manipulate this object in a separate thread.
I'm using [NSThread detachNewThreadSelector...] to run the 'doJob' function.
In this case my delegate method not being called. I guess this is because 'self' points to new thread instead of main one. Ok. I'm passing self as argument to function while creating the thread and it still not working. What do i miss?
my current code is as follows:
- (void)mainFunction
{
[NSThread detachNewThreadSelector:#selector(doJob:) toTarget:self witObject:self];
}
- (void)doJob:(MyObject*)parentThread
{
ManipulatedObject *obj = [[ManipulatedObject alloc] init];
obj.delegate = parentThread;
[object startJob];
}
GCD will make most of your multi-threading troubles trivial. You can do something like this:
- (void)mainFunction
{
// Runs your task on a background thread with default priority.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
ManipulatedObject * obj = [[ManipulatedObject alloc] init];
[obj startJob]; // I'm assuming this is sychronous.
// The callback is explicitly run on the main thread.
dispatch_async(dispatch_get_main_queue(), ^{
// Your callback here.
[obj release];
});
});
}
That's all you have to do, it's that simple. All the relevant code is inline and together.
If you want the ManipulatedObject to explicitly invoke the block, then you could add that ability to ManipulatedObject. To do so, you should:
Define the block type for convenience typedef void(^MyCallback)();
Add #property (nonatomic, copy) MyCallback block; and #synthesize block. Don't forget the copy.
Invoke the block when you need to dispatch_async(dispatch_get_main_queue(), [self block]);.
If your delegate needs to make more than one kind of callback, then you will need a block for each callback. It's a minor inconvenience, but it's worth it for all the conveniences you gain.
For a more thorough explanation of blocks and GCD, check out WWDC 2011 session 308.
Well firstly you do not need to pass self as the witObject: parameter, (which is spelt wrong) because - (void)doJob:(MyObject*)parentThread is still in the same object (self is the same in both threads), self has nothing to do with your main thread its MyObject presumable, you also have a problem were you are not creating a new autorelease pool for your doJob:, doJob: should look like
- (void)doJob:(MyObject*)parentThread
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
ManipulatedObject *obj = [[ManipulatedObject alloc] init];
obj.delegate = parentThread;
[object startJob];
[pool release];
}
you have to give us some information about how you're delegate method is being called, if it is tying to use timers or something like that then you are going to have problems because there is no runloop to add your timer to.

Objective-C performSelector when to use colon?

Do you only postfix the method name with a : if you are calling a foreign object?
For some reason
[self performSelector:#selector(myMethod:) withObject:nil afterDelay:5];
Does not work but
[self performSelector:#selector(myMethod) withObject:nil afterDelay:5];
Does!
EDIT:
Declared in the implementation of a class but not the interface.
- (void)myMethod
{
// Some stuff
}
The colon represents a method argument. Since myMethod takes no arguments its selector can't have a colon. If you had multiple arguments like this...
- (void)myMethod:(id)method object:(id)object enabled:(BOOL)bool {
// Some Stuff
}
... the selector would be #selector(myMethod:object:enabled:)
In Objective-C the colons are part of the method name. That is, myMethod and myMethod: are distinct selectors (and in your case, only the latter exists).
For instance, for a method declared like:
-(void)doSomethingWithFoo:(int)foo andBar:(int)bar;
The selector is doSomethingWithFoo:andBar:.