just a quick question...say I have created a button and added some action to it:
-(IBAction)btnclicked:(id)sender {
//some code here
}
And now I wanna call it so i can do this:
[self btnclicked:self];
So my question is what role does the self after the btnclicked play? why will it be wrong to write it this way:
[self btnclicked:sender];
please help.
sender is type id, which can be any objective-c object (or nil). So you can pass whatever you like into the method and the compiler will be happy.
what you do inside that method may be excpeting a UIControl of some sort though, so if you pass in a UIView or NSString or anything else, there could be an unrecognized selector or some other crash. In other words, you can pass in what you like, as long as you know what you're doing.
for your question about it being wrong to pass in sender. I am guessing that is giving a compiler warning because sender is undefined in the context you are using it. sender is the variable name given to the argument inside the method, it is not a global variable or constant that can be passed around
Related
I have a property, that in Objective-C I created like this:
self.myProperty = [[MyClass alloc] initWithCompletionBlock:^(MyClass *object) {
dispatch_async(dispatch_get_main_queue(), ^{
[self doSomethingAfterInitialization];
});
}];
And it worked great. Initialization of the MyClass object could create an indeterminate amount of time, so I passed a completionHandler in to it. When it finished, doSomethingAfterInitalization: would handle business.
Now in Swift, I'm trying to create the same object and assign it to a property, with problems.
The property never will change, so it makes sense to me to create it as a Swift constant.
So I'm trying it like this:
let myProperty = MyClass(completionBlock:{ (MyClass) -> (Void) in dispatch_async(dispatch_get_main_queue(), doSomethingAfterInitialization())})
To me that seemed like a direct translation... but the Swift compiler tells me that's not correct, via the error
Use of instance member 'doSomethingAfterInitialization' on type 'MyViewController'; did you mean to use a value of type 'MyViewController' instead?
Well that didn't help much. So instead I tried changing the call to the doSomethingAfterInitialization function to self. doSomethingAfterInitialization(), in which case I see
Value of type '(NSObject) -> () -> TodayWidgetTableViewController' has no member 'doSomethingAfterInitialization'
Any idea how I can fix this? Obviously my initializer is a little weird in the first place, so I'm wondering if this is something that doesn't really translate at all to Swift.
I have an IBAction like:
- (IBAction)thisThing:(id)sender {
[self doSomething];
}
I would like do this (manually call the IBAction):
[self thisThing];
However I obviously need to do [self thisThing:...];. <- (what the heck goes after the colon?)
I'm not sure what (id)sender is supposed to be. How do call it manually without needing to click the button that it's tied to? I searched for anything about IBAction (id)sender and the results came up completely empty.
what the heck goes after the colon?
Well it depends on how you have written code inside the IBAction. Say for a calculator app, if all buttons are hooked up with the same IBAction then you would need sender (in this case NSButton) to identify which button got touched/clicked.
-(IBAction) buttonClicked:(id) sender {
// sender's identifier or Tag will let us know the number clicked here
[self doSomeThing];
}
But if you had IBActions for each and every button you would not need to be dependent on sender.
-(IBAction) firstButtonClicked:(id)sender;
-(IBAction) secondButtonClicked:(id)sender;
and so on ...
So in the first case if I want to programatically invoke the action I would pass the sender with appropriate attributes set to make sure the correct button got clicked. In second case just pass nil as it does not depend upon sender's value.
While popeye's comment answers your question, here's another perspective.
You have complete control over the action method. If you aren't using the sender parameter for anything in that method, then you do not have to supply it when calling it manually. By not supply it I mean pass nil as the value of the parameter.
Normally, it will contain a pointer to the control that is wired up to the action. If you did want to use if for something, they you would simply cast sender to the type of the control and do whatever with it.
- (IBAction)thisThing:(id)sender
In here,
- denotes the start of a instance method, whereas + means class(static) method.
( .. ) indicate return type. IBAction is actually void. Using IBAction instead of void tells that this method will be associated with UI(.nib) events.
thisThing is the name of the method, followed by parameter list.
In C view point, actual function names is something like thisThing:. That is, number of parameter modifies function name (external linkage).
If you meant to call thisThing: but write [self thisThing], you are calling different (not existing) method.
So, you must write :. What actual value to pass? One can decide this by looking at the method implementation.
If you have the IBOutlet of the button, you can pass that like [self thisThing:btn];
Or simply pass nil, [self thisThing:nil]; (if you are not using sender inside the IBAction)
- (IBAction)thisThing:(id)sender {
}
Is an event handler. That means that it is called when an event is sent by someone. A typical example of an event is a click on a button, it that case, the button sends the event (that means the button, a NSButton instance, is the sender).
Having the sender as a parameter is useful when you use the same event handler for events coming from different sources, e.g.
- (IBAction)buttonTapped:(id)sender {
if (sender == self.myButton1) {
//button 1 was tapped
}
else if (sender == self.myButton2) {
//button 2 was tapped
}
}
If this case, if you want to call the event handler manually, you just call
[self buttonTapped:self.myButton1];
If you don't use the sender parameter, then you can simply call
[self buttonTapped:nil];
However, the parameter is completely optional, so you can eliminate it:
- (IBAction)buttonTapped {
// ...
}
[self buttonTapped];
On a separate note, it's never good to call event handlers manually. Event handlers serve to handle events. If you need to perform the same action manually, separate it from the event handler, e.g.
- (IBAction)buttonTapped {
[self doSomething];
}
instead of calling [self buttonTapped], call [self doSomething]
I have a selector declared with id sender, like this:
- (void)fbLoginClicked:(id)sender
{ }
I want to call it from another method like this:
[self fbLoginClicked];
But,I'm getting this error:
No visible selector 'fbLoginClicked'
How can I fix this?
The method signature requires that you pass a parameter (sender) to the method.
I'm assuming that this method is also an IBAction, hence why it requires a sender parameter.
To call this method through code you need to pass it a parameter, assuming that parameter is not used then you can simply call:
[self fbLoginClicked:nil];
If this method is not an IBAction and nothing is done with the sender parameter, then you could change the method signature to:
- (void)fbLoginClicked
Is there any way I can test if a method exists in Objective-C?
I'm trying to add a guard to see if my object has the method before calling it.
if ([obj respondsToSelector:#selector(methodName:withEtc:)]) {
[obj methodName:123 withEtc:456];
}
There is also the static message instancesRespondToSelector:(SEL)selector
You would call it like this:
[MyClass instancesRespondToSelector:#selector(someMethod:withParams:)]
or like this:
[[myObject class] instancesRespondToSelector:#selector(someMethod:withParams:)]
This may be useful if you would like to call one constructor or another one depending on this (I mean, before having the instance itself).
Use respondsToSelector:. From the documentation:
respondsToSelector:
Returns a Boolean value that indicates whether the receiver implements or inherits a method that can respond to a specified message.
- (BOOL)respondsToSelector:(SEL)aSelector
Parameters
aSelector - A selector that identifies a message.
Return Value
YES if the receiver implements or inherits a method that can respond to aSelector, otherwise NO.
You're looking for respondsToSelector:-
if ([foo respondsToSelector: #selector(bar)] {
[foo bar];
}
As Donal says the above tells you that foo can definitely handle receiving the bar selector. However, if foo's a proxy that forwards bar to some underlying object that will receive the bar message, then respondsToSelector: will tell you NO, even though the message will be forwarded to an object that responds to bar.
Checking selectors with respondsToSelector is normally only for delegate methods. You shouldn't be using forwardInvocation or proxies for delegate methods. If you need to use respondsToSelector in other situations you might want to make sure that there isn't a more appropriate way to design your program.
I want to build a selector from a class method.
I'm doing it this way:
NavigationTreeActionHandler* handler=[NavigationTreeActionHandler self];
NavigationTreeNode* bombsNode=new NavigationTreeNode("Bombs","bigbomb.tif"
,handler,#selector(BigBombButtonPressed:));
I need to pass to NavigationTreeNode the target and the selector to the target method.
I try to get the target using the self property of the class object (Don't know if htis is the correct way to do it). Then I get the selector for the class method I want to call on the class.
Everything compiles ok but it fails when I use:
[[handler class] instanceMethodSignatureForSelector:selector];
I get a nil and don't really know why... could anybody help please?
A few suggestions:
[NavigationTreeActionHandler self] will work fine to get the class object, but I would declare handler as type id instead of NavigationTreeActionHandler*, because it's not a reference to an instance that type
[handler class] is redundant; handler is already the class object.
instanceMethodSignatureForSelector: is only going to return nil if handler does not implement the selector. Verify your spelling etc., and try throwing in an NSLog to verify what you're receiving:
NSLog("handler = %# sel = %#", handler, NSStringFromSelector(selector));
But I'm unclear on what you're trying to do with instanceMethodSignatureForSelector: in the first place. If you're just trying to call the class method, wouldn't [handler performSelector:selector]; do what you want?
It's likely you're using code that assumes that calling the method class will get an object's class, and thus uses [[handler class] instanceMethodSignatureForSelector:selector] to get the method signature for the selector on the object handler. While that works for normal objects, that does not work for class objects, because of the different meaning of the class method on class objects, since the class method +class overrides the instance method -class, and +class simply returns the object it's called on, instead of the class it's an instance of (a meta-class).
The solution is that you don't even need to get the class at all, as there is a method you can call on an instance to get its method signature:
[handler methodSignatureForSelector:selector]
This is a much shorter and simpler way of doing what was intended, and also works for class objects too.