Why is perform:withObject method not found? - objective-c

I want to fix warnings in my application code. I have an AddressBookModel.h which implements the TTModel protocol.
You find both interface and implementation of the AdressBookModel in the answer of this question. This is exactly how I implemented it How to use Three20 TTMessageController?
However for
[_delegates perform:#selector(modelDidStartLoad:) withObject:self];
and some other similar selectors I get warnings like
Method -perform:withObject not found (return type defaults to id)
Since _delegates is an array
- (NSMutableArray*)delegates {
if (!_delegates) {
_delegates = TTCreateNonRetainingArray();
}
return _delegates;
}
some suggested to use makeObjectsPerformSelector but this gives me an unrecognized selector sent to instance exception.
Here is the TTModel source code: http://api.three20.info/protocol_t_t_model-p.php
Why is perform:withObject missing? Is performSelector:withObject an alternative (my app crashes using it)?

_delegates is an array of delegates. It is not a true delegate, as signified from the name which is in plural form. An array does not respond to the -modelDidFinishLoad: method — its elements do.
You need to take each element out of the array and call the method of them, e.g.
for (id<TTModelDelegate> delegate in _delegates)
[delegate modelDidFinishLoad:self];
or even easier, using NSArray's -makeObjectsPerformSelector:…:
[_delegates makeObjectsPerformSelector:#selector(modelDidFinishLoad:)
withObject:self];

perform:withObject: method that produces this warning is defined in NSArray(TTCategory) category in NSArrayAdditions.h file in Three20 framework. You need to ensure that this header is imported/referenced properly by compiler, i.e. you need to look at importing this specific header or check your Three20 integration configuration.
You do not need to change this method to makeObjectsPerformSelector: since this is just an import problem (your code runs fine but just produces compile warnings).

Reading between the lines, it looks like you want the objects that are in your _delegates array to all perform a particular selector. You need to call -makeObjectsPerformSelector:withObject: like this:
[_delegates makeObjectsPerformSelector: #selector(modelDidCancelLoad:) withObject: self];

You are mistyping modelDidCancleLoad: should be modelDidCancelLoad:
'NSInvalidArgumentException', reason:
'-[__NSCFArray modelDidCancleLoad:]: unrecognized selector
sent to instance 0x24f480'
Make sure your _delegates is what you are expecting it to be. It seems to be an NSArray.

Related

Why don't selectors need to pass arguments to methods that take parameters?

I'm looking into a project, trying to get a grip on the code. I just ran in to a #selector. As far as I understand it, selectors call methods (I'm sure there's more to it, of course).
One thing that struck me is that the selector call doesn't pass an argument to the method even though the method itself takes a parameter. The code works fine, so I take it that this is normal behavior with a selector. Here is a sample of the code:
[Communication sendVerifyNumber:ps.address verificationCode:#""
withCallbackMethod:#selector(numberVerificationCallCompleted:)
callbackFailMethod:#selector(numberVerificationCallFailed:) onObject:self];
}
And the numberVerificationCallCompleted:
-(void)numberVerificationCallCompleted:(NSNumber*)responseNumber{...}
So, my question is, is this a thing with selectors. I mean, is that why they are used? And how does it work? The method still needs an argument to work properly, so it has to come from somewhere, right?
A selector describes a method within an Objective-C class, allowing you to call it at a later point in time. Parameters are passed when it's called, not when it's described.
You store selectors in a SEL type and call them using performSelector: making them ideal to use as callbacks.
The method you show above, will do something like this:
- (void)sendVerifyNumber:(NSString*)address
verificationCode:(NSString*)verificationNumber
withCallbackMethod:(SEL)callbackMethod
callbackFailMethod:(SEL)failedCallbackMethod
onObject:(id)callbackObject
{
BOOL success = doWhateverIDo();
if (success) {
[callbackObject performSelector:callbackMethod
withObject:someArgumentToPassback];
} else {
[callbackObject performSelector:failedCallbackMethod
withObject:someArgumentToPassback];
}
}
Note: you can only call a method that is described by a selector if it takes zero, one or two parameters (using performSelector:, performSelector:withObject: and performSelector:withObject:withObject: respectively).

Objective-C Passing Method Reference As Parameter

I've seen a lot of discussions NEAR this subject, but none that actually work in Xcode 5.x, especially using ARC. I have a simple problem:
I need to pass a method reference to a CreateButton method so that when the button is called it calls my custom function, and not some generic one.
I've tried using (SEL) type, but that doesn't work with ARC. I've tried using the &func method, but that claims I haven't declared the function yet.
So my need is:
Class A calls Class B and sends over the info to make a UIButton. Within that call, I want to send over the action:method in a reference. I'm sure this is done routinely, but I can't seem to find an iOS 7 / Xcode 5.x method of doing it. I've also reviewed the O'Reilly iOS 7 fundamentals and cookbook code and couldn't find this discussed anywhere.
Thanks for you help.
When I have to pass selectors around, I convert them to strings with NSStringFromSelector() and back to selectors with NSSelectorFromString().
Passing the strings around is a lot easier. You can store them in collections (arrays, dictionaries), serialize and unserialize them, and they will work naturally with ARC.
Example:
In your class A where you gather the information to create a button:
NSString *selectorString = NSStringFromSelector(#selector(yourActionMethodNameHere:));
// Gather more information needed by Class B here, then package
// it all up into a dictionary, for example
NSDictionary *buttonInfo = #{#"selectorString": selectorString, /* more stuff here */};
At this point, you can call your button-constructing method in Class B, passing along buttonInfo, which contains all the information that that helper method needs, including the selector. The method can convert the string back to a selector and use it like this:
SEL actionSelector = NSSelectorFromString(buttonInfo[#"selectorString"]);
// configure your button to use actionSelector here
You should be able to use SEL parameters? I know I have done before.
ARC might complain and give you a warning, but it won't fail to compile. It's simply a warning because it can't quite figure out what to do memory wise.
If you really can't get that to work though, another alternative would be to use a block, so you might call your method like
[objectA performMethodWithParam:paramA paramb:paramB completion:^{ ... do somethhing ... }];
Then in that method you can just call
completion();
Instead of actually calling a method.
Another alternative would be to use the delegate pattern. Create a #protocol defining a method such as classADidFinish then make class B implement that method. Then set the instance of classB as the delegate for your classA instance, and have it call that method when it's done.
Both of these approaches will stop ARC moaning at you.
But as I said, using SEL params should work fine. There is a way you can even get the compiler to stop showing you the warnings but it's a little ugly.

Work around of "No known instance method for selector" error for id type

My iPhone app has a login view controller to pop up whenever it is necessary to login. After the user loged in, I have this:
if ([self.presentingViewController respondsToSelector:#selector(userDidLogin)]) {
[((id)self.presentingViewController) userDidLogin];
} else {
[self.presentingViewController dismissModalViewControllerAnimated:YES];
}
However, the compiler kept complaining about "No known instance method for selector userDidLogin". Then I added an instance method named userDidLogin for the login view controller, which was of course not self.presentingViewController, then the build succeeded.
This workaround feels unreasonable to me. Is it a bug in Xcode or intended behavior? Is it is the latter, what is the rationale?
The compiler needs to know the return type of the userDidLogin selector so that it can generate correct code:
If the message returns a struct, the compiler may need to generate a call to objc_msgSend_stret. (Source: Greg Parker's blog.)
If the message returns a floating-point number, the compiler needs (on some platforms) to generate a call to objc_msgSend_fpret. (Source: Greg Parker's blog.)
Otherwise, the compiler needs to generate a call to objc_msgSend.
The userDidLogin selector has no arguments, but if the selector did have arguments, the compiler would also need to know the declared argument types so it could pass the arguments correctly.
Additionally, if you're using ARC, the compiler needs to know the return type and ownership annotations of the selector so that it can generate a release of the return value if appropriate.
The usual way to handle this is just to #import the header file of the class that declares the userDidLogin message. As long as the compiler has seen the selector declared somewhere, it won't complain about sending it to an id.

Objective-C respondsToSelector

From what I have learned so far: In Objective-C you can send any message to any object. If the object does implement the right method it will be executed otherwise nothing will happen. This is because before the message is sent Objective-C will perform respondsToSelector.
I hope I am right so far.
I did a little program for testing where an action is invoked every time a slider is moved. Also for testing I set the sender to NSButton but in fact it is an NSSlider. Now I asked the object if it will respond to setAlternateTitle. While a NSButton will do and NSSlider will not. If I run the code and do respondsToSelector myself it will tell me the object will not respond to that selector. If I test something else like intValue, it will respond. So my code is fine so far.
- (IBAction)sliderDidMove:(id)sender
{
NSButton *slider = sender;
BOOL responds =
[slider respondsToSelector:#selector(setAlternateTitle)];
if(responds == YES)
{
NSLog(#"YES");
}
else
{
NSLog(#"NO");
}
[slider setAlternateTitle:#"Hello World"];
}
But when I actually send the setAlternateTitle message the program will crash and I am not exactly sure why. Shouldn't it do a respondsToSelector before sending the message?
First of all, the name of a method (its selector) includes all subparts and colon characters, as mvds said.
Second of all, the method -respondsToSelector: is not called by the runtime, it's usually called by the user (yourself or APIs that want to know if a delegate, for example, responds to an optional method of the protocol).
When you send a message to an object, the runtime will look for the implementation of the method in the class of the object (through the object's isa pointer). It's equivalent to sending -respondsToSelector: although the message itself is not dispatched. If the implementation of the method is found in the class or in its superclasses, it's called with all the arguments you passed in.
If not, then the runtime gives the message a second chance to be executed. It will start by sending the message + (BOOL)resolveInstanceMethod:(SEL)name to the class of the object: this method allows you to add the method at runtime to the class: if this message returns YES, it means it can redispatch the message.
If not it gives the message a third chance to be executed, it sends - (id)forwardingTargetForSelector:(SEL)aSelector with the selector, this method can return another object that may be able to respond to the selector on behalf of the actual receiver, if the returned object can respond, the method is executed and the value is returned as if it was returned by the original message. (Note: This is available beginning with OS X 10.6 or iOS 4.)
If the returned object is nil or self (to avoid infinite loops), the runtime gives the message a fourth chance to execute the method… It sends the message - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector to get a method signature in order to build an invocation. If one is provided then an invocation is sent through the message - (void)forwardInvocation:(NSInvocation *)anInvocation. In this method you can parse the invocation and build other messages to send to other targets in any ways you want, and then you can set the return value of the invocation… That value will act as the return value of the original message.
Finally, if no method signature is returned by the object, then the runtime sends the message - (void)doesNotRecognizeSelector:(SEL)aSelector to your object, the implementation of this method in NSObject class throws an exception.
For one thing, the selector is not only the "name" of the message, but also what follows, i.e. the arguments, and their names.
So the correct selector for some -(void)setAlternateTitle:(NSString*)str would be
#selector(setAlternateTitle:)
with the :
As for your problem: If a class respondsToSelector() and you perform that selector, you shouldn't get a crash on sending an unknown selector. What kind of crash log do you see in the debugging window?
(ps. why not include the [slider setAlternateTitle:...] in the if ( responds ) { ... } conditional block?)
"This is because before the message is
sent Objective-C will perform
respondsToSelector."
I guess this is not correct. If the object does not respond to selector, it will crash at runtime. There is no automatic checking by the system. If there was a check by the run time system then we should never get "unrecognized selector sent to instance" exception.
Please make me correct if I am wrong.
EDIT: This is not a straight forward crash, but the default result is the process will be terminated. The whole sequence is already explained in comment and other answer, so I am not going to write that again.
There is an +instancesRespondToSelector: method. As the name suggests, it tells you whether the instances of the class implement that method.

NSClassFromString returns nil

Why does NSClassFromString return nil ? As per the definition it has to return class name.
How should I take care to rectify this problem? I need to instantiate a class from string and call the method, which is in the class, using the instance created.
This is how my code looks like:
id myclass = [[NSClassFromString(#"Class_from_String") alloc] init];
[myclass method_from_class];
But the method_from_class function is not being called, control is not going into it. And my code is error free. Any idea how to solve this in Objective-C?
If you are trying to instantiate a class from a static library, you must add the "-ObjC" flag to the "Other Linker Flags" build setting.
The Documentation for the function says:
Return Value
The class object named by
aClassName, or nil if no class by that
name is currently loaded. If
aClassName is nil, returns nil.
An example of how this should be properly used is as follows:
Class dictionaryClass = NSClassFromString(#"NSMutableDictionary");
id object = [[dictionaryClass alloc] init];
[object setObject:#"Foo" forKey:#"Bar"];
It is possible that your class is not getting linked if this is the only reference to it.
I had a factory method to instantiate various types of subclass. The factory had a switch statement that went to the appropriate subclass and alloc'd and init'ed it. I noticed that all of the alloc/init statements were exactly the same, except for the name of the class. So I was able to eliminate the entire switch block using the NSClassFromString() function.
I ran into the same problem - the return was nil. This was because the class was not used elsewhere in the program, so it wasn't getting linked, so it could not be found at runtime.
You can solve this by including the following statement:
[MyClass class];
That defeats the whole purpose of what I was trying to accomplish, but it might be all you need.
This happened to me when I add an external file to the Xcode project. Adding the .m file to Build Phases > Compile Sources solve the problem.
You also need to make sure the class you are trying to instantiate is included in the project. If you added it later, you made need to click the checkbox next to the Target you are building.
Why not decomposing all these calls ? This way, you can check the values between the calls:
Class myclass = NSClassFromString(#"Class_from_String");
id obj = [[myclass alloc] init];
[obj method_from_class];
By the way, is method_from_class an instance method or a class method ? If it is the later, then you can directly call method_from_class on the myclass value:
[myclass method_from_class];
I also saw an oddity where adding the standard singleton code espoused by apple prevented the class from being loaded. The code was working as expected, then I added the singleton, and suddenly the NSClassFromString started returning nil. Commenting out the singleton code resulted in the NSClassFromString resolving the class correctly. I don't understand the interaction, but I think the singleton static var was somehow getting mangled to hide the class name...?