Selector in Method over riding - ios

In the case of method over riding in objective c how selector knows that which method needs to call via selector?
As we dont pass any arguments in slector section...
Ex:
in tmp.m file
There is 2 methods with different arguments
-(void)details
{
}
-(void)details:(NSDictionary *)result
{
}
And when m call another method with the use of selector as:
[mc detailstrac:[[NSUserDefaults standardUserDefaults] valueForKey:#"userID"] tracid:self.trac_id selector:#selector(details:)];
How selector knows to call which method !
I have checked that
-(void)details:(NSDictionary *)result
{
}
this method is called every time then what about
-(void)details
{
}
this ?

Selector will know on the basis how you call the method like from your example,
[mc detailstrac:[[NSUserDefaults standardUserDefaults] valueForKey:#"userID"] tracid:self.trac_id selector:#selector(details:)];
when you call #selector(details:) then the selector will call this method
-(void)details:(NSDictionary *)result { }
And When you call #selector(details) then the selector will call
-(void)details { }
The main difference here is #selector(details) and #selector(details:).
Hope you understand my point!
Happy Coding!

Related

Objective-C: Calling selectors with variable arguments

I'm facing the following problem and I already tried a lot. I have also read the others Questions in Stackoverflow like:
Objective-C: Calling selectors with multiple arguments
and the Cocoa Core Competencies about Selectors, but I'm searching for the best way to pass a variable of arguments to a selector.
-(void) runAllStatusDelegates : (SEL)selector
{
for (NSValue *val in self.statusDelegates)
{
id<StatusDelegate> delegate = val;
if ([delegate respondsToSelector:selector])
{
[delegate performSelector:selector];
}
}
}
This method is responsible to call the methods inside the delegates. The parameter is a Selector. My Problem is that the selector can have 0 - 3 arguments, as shown below.
-(void) handleBluetoothEnabled:(BOOL)aEnabled
{
if (aEnabled)
{
[self.statusDelegate bluetoothEnabled];
if (_storedPenSerialNumber != nil && ![_storedSerialNumber isEqual:kUnknownPenID])
{
[self runAllStatusDelegates: #selector(penConnected : _storedSerialNumber : _storedFirmware:)];
}
}
else
{
[self.statusDelegate bluetoothDisabled];
}
}
-(void) handleChooseDevice:(BluetoothDeviceList*)aDevices
{
NSLog(#"Handle Choose Device");
[self runAllStatusDelegates: #selector(chooseDevice:aDevices:)];
}
-(void) handleDiscoveryStarted
{
NSLog(#"Discovery Started");
[self runAllStatusDelegates: #selector(searchingForBluetoothDevice)];
[self.statusDelegate handleStatus:#"Searching for your digipen"];
}
This implementation isn't working because the performSelector is not recognizing the selector.
I also tried to implement it with #selector(penConnected::) withObject:_storedSerialNumber but then I have to implement another method with additional arguments as well and I don't want that.
I'm new to objective-c so I'm not so familiar with all possibilities.
My idea is to pass a String and an Array of arguments to runAllStatusDelegates and build up the selector inside that method, but is this the best way or are there more convenient ways?
I am personally not a fan of NSInvocation for complex signatures. Its really great for enqueueing a simple function call on a queue and running it when you need it but for your case, you know the selector so you don't really need to go the invocation route. I typically find invocations are more useful if you don't actually know the selector you want to call at compile time, maybe its determined by your API etc.
So what I would do is simply pass a block into your runAllStatusDelegates method that will execute against all your delegates:
- (void)performSelector:(SEL)selector againstAllDelegatesWithExecutionBlock:(void (^)(id<StatusDelegate>))blockToExecute
{
for (id<StatusDelegate> delegate in self.statusDelegates)
{
if ([delegate respondsToSelector:selector])
{
blockToExecute(delegate);
}
}
}
Then when you want to call your delegates with a function it looks like this:
[self performSelector:#selector(handleAnswerOfLifeFound)
againstAllDelegatesWithExecutionBlock:^(id<StatusDelegate> delegate){
[delegate handleAnswerOfLifeFound];
}];
I guess the only downside might be that you could change the selector and pass a different function into the block. How I would solve this is by actually making sure not all methods are optional, or if they are optional to make the actual check inside the block, this would clean up the signature:
- (void)callAllDelegatesWithBlock:(void (^)(id<StatusDelegate>))blockToExecute
{
for (id<StatusDelegate> delegate in self.statusDelegates)
{
blockToExecute(delegate);
}
}
and then your actual usage for an optional method:
[self callAllDelegatesWithBlock^(id<StatusDelegate> delegate){
if([delegate respondsToSelector:#selector(handleAnswerOfLifeFound)]){
[delegate handleAnswerOfLifeFound];
}
}];
Still error-prone but at least a bit tidier.
You can use NSInvocation for this case
SEL theSelector = #selector(yourSelector:);
NSMethodSignature *aSignature = [NSMethodSignature instanceMethodSignatureForSelector:theSelector];
NSInvocation *anInvocation = [NSInvocation invocationWithMethodSignature:aSignature];
[anInvocation setSelector:theSelector];
[anInvocation setTarget:self];
[anInvocation setArgument:&arg1 atIndex:2];
[anInvocation setArgument:&arg2 atIndex:3];
[anInvocation setArgument:&arg3 atIndex:4];
[anInvocation setArgument:&arg4 atIndex:5];
//Add more
Note that the arguments at index 0 and 1 are reserved for target and selector.
For more info http://www.cocoawithlove.com/2008/03/construct-nsinvocation-for-any-message.html
you can binding the arguments to the selector
NSDictionary *argInfo=#{#"arg1":arg1,#"arg2":arg2,...};
objc_setAssociatedObject(self,#selector(chooseDevice:aDevices:),argInfo,OBJC_ASSOCIATION_COPY)
[self runAllStatusDelegates: #selector(chooseDevice:aDevices:)];
then in the
-(void) runAllStatusDelegates : (SEL)selector
{
for (NSValue *val in self.statusDelegates)
{
id<StatusDelegate> delegate = val;
if ([delegate respondsToSelector:selector])
{
NSDictionary *argInfo=objc_getAssociatedObject(self, selector);
//call the fun use arginfo
}
}
}

What happens when a parametrized selector is called with no parameters?

I was going over this example in which a selector is used. I have copied the code from there for convenience.
// MYTapGestureRecognizer.h
#interface MYTapGestureRecognizer : UITapGestureRecognizer
#property (nonatomic, strong) NSString *data;
#end
// MYTapGestureRecognizer.m
#implementation MYTapGestureRecognizer
#end
// =====================
....
MYTapGestureRecognizer *singleTap = [[MYTapGestureRecognizer alloc] initWithTarget:self action:#selector(tapDetected:)];
singleTap.data = #"Hello";
.....
// ====================
-(void)tapDetected:(UITapGestureRecognizer *)tapRecognizer {
MYTapGestureRecognizer *tap = (MYTapGestureRecognizer *)tapRecognizer;
NSLog(#"data : %#", tap.data);
}
My question is
1-When self calls the selector what parameter does it pass in the above case ?
2- Also if a selector (pointing to a method that requires parameters) is called (see example below) and no parameters are passed are there any defaults in that case ? If possible is there any documentation for that ?
Suppose the signature of MyTest is
- (void) MyTest : (NSString*) a;
Now constructing and calling a selector
SEL a = NSSelectorFromString(#"MyTest:");
[t performSelector:a]; //Works Fine and the call is made - However Notice no parameter is passed . In this case what would the value of the parameter be in the method ?
I checked the following but I could not find this information
Apple docs
Rys Tutorials
Answers to your questions:-
When self calls the selector what parameter does it pass in the above case ?
If a tap is detected and the selector is called, the parameter will be an object of UITapGestureRecognizer. This will be the same instance on which the tap gesture is detected.
Also if a selector (pointing to a method that requires parameters) is called (see example below) and no parameters are passed are there any defaults in that case ? If possible is there any documentation for that ?
Why do you want to call the method like that, is there any special purpose?. If not, you can call the method just like
[self tapDetected:nil];
or
[self performSelector:#selector(tapDetected:) withObject:nil];
If you call the method as provided in the question, most probably it will crash.
If you wish to call the method on self, pass nil parameter to it. But i do not understand what purpose is it serving you.
Also if you do not send parameters to your methods, it is going to fail at your builds. You have to pass either the parameter or nil.
Also if your method does not accept nil parameters it might cause an exception - 'NSInvalidArgumentException'
Normally if you want to access that selector via self, use it like :
[self tapDetected:nil];
You need to handle this case in your selector, like :
-(void)tapDetected:(UITapGestureRecognizer *)tapRecognizer {
if (tapRecognizer)
{
MYTapGestureRecognizer *tap = (MYTapGestureRecognizer *)tapRecognizer;
NSLog(#"data : %#", tap.data);
}
else
{
//Do your work
}
}
Also not only this, if you are not sure of parameter you are passing change your selector decalartion as id, like :
-(void)tapDetected:(id)sender {
NSString *className = NSStringFromClass([id class]);
NSLog(#"Object passed is of class : %#", className);
//And make check here
if ([id isKindOfClass:[MYTapGestureRecognizer class]])
{
//Do your work here
}
}
There are no default cases, you need to handle every case manually or else app will crash.
SEL a = NSSelectorFromString(#"MyTest:");
[t performSelector:a]; //Works Fine and the call is made - However Notice no parameter is passed . In this case what would the value of the parameter be in the method ?
It will be undefined junk. You have no guarantees about what it might contain. Most likely, it will be an invalid pointer. If you're unlucky, it might be a valid pointer to some arbitrary object and operating on it will corrupt your app's state. If you're lucky, it will crash so you can find the problem.

Is it possible to get arguments from selector variable?

Is it possible to get the argument from the selector varibale.
For Example
-(void)methodTest:(NSString*)someArg{
SEL selector = #selector(methodTest:);
[self testCall:selector];
}
-(void)testCall:(SEL)selectorArg{
//I would like to get the parameter from the selector (selectorArg)
}
My Questions:
1. Does the selector has the argument, someArg? If not, how to create the selector variable with argument.
2. What is the other way around to do the same?
Just curious to know.
If you create a selector like:
SEL selector = #selector(methodTest:);
It means (Note the :) that the selector expects an argument.
You can pass argument to such method like:
[self performSelector:selector withObject:argument afterDelay:0.0];
And the method syntax will be:
- (void)methodTest:(id)someArgument;
If the selector is created by the following syntax:
SEL selector = #selector(methodTest);
Then you can't pass any argument to this selector.
You can call like:
[self performSelector:selector withObject:nil afterDelay:0.0];
And the method syntax will be:
- (void)methodTest;
I'm not totally understand what you searching for, but I'm guessing you need
performSelector method. Please check this SO thread: iOS - How to implement a performSelector with multiple arguments and with afterDelay?

Unrecognized selector sent to instance on if(bool)

I know this questions has been posted a lot, but they are all very specific and do not apply to my problem.
[MirrorStarAV startRecording:]: unrecognized selector sent to instance 0x15561d60
I get this error when calling the following method:
- (bool) startRecording {
bool result = NO;
#synchronized(self) {
if (!_recording) { //Exception is raised on this line
result = [self setUpWriter];
startedAt = [[NSDate date] retain];
_recording = YES;
}
}
return result;
}
I call the method as following:
bool _startRecording(){
return [delegateMSA startRecording];
}
[MirrorStarAV startRecording:]: unrecognized selector sent to instance 0x15561d60
The above error message is basically saying that MirrorStarAV doesn't respond to startRecording: so when you call [delegateMSA startRecording]; it is crashing. The delegateMSA has an instance of MirrorStarAV set to it but instances of MirrorStarAV don't respond to `startRecording:
Whilst YES it would be better for you to change your method to something like
bool _startRecording(){
if ([delegateMSA respondsToSelector:#selector(startRecording)])
{
return [delegateMSA startRecording];
}
return false;
}
but this isn't your issue. Note that [MirrorStarAV startRecording:] has a colon : at the end. You are calling startRecording: somewhere over startRecording
Check before, whether the delegate responds to this method, so you can make sure that the object is also able to respond to this selector:
bool _startRecording(){
if ([delegateMSA respondsToSelector:#selector(startRecording)])
{
return [delegateMSA startRecording];
}
return false;
}
Either way, I would recommend to add this selector to the delegates methods with #required:
#protocol MyRecorderDelegate <NSObject>
#required
- (BOOL)startRecording;
#end
So, you would get a compiler warning, if your delegate was not implementing this method.
Verify if the delegateMSA is an instance of class where you have the startRecording method.
Or do something like
bool _startRecording(){
if([delegateMSA respondsToSelector:#selector(startRecording)]) {
return [delegateMSA startRecording];
}
}

Executing code defined in a string

I would like to be able to execute a piece of code that is defined in a string. I am aware of performSelector: but the object that will perform the selector is going to be different.
Example strings
[[AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo] hasFlash]
[UIImagePickerController isCameraDeviceAvailable:UIImagePickerControllerCameraDeviceFront]
So what I would like to do is something along the lines of
SEL selector = NSSelectorFromString(#"[[AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo] hasFlash]");
if (selector) {
// Show flash buttons
}
You can not fire a selector that calls a nested method call.
Selectors are only method names with showing number of arguments as method:abc:yxa:
As the statement below:
SEL selector = NSSelectorFromString(#"[[AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo] hasFlash]");
is calling
[AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]
then
[objectReturnedByAbove hasFlash]

Resources