What is the point of an optional method in a protocol?
If I'm going to have to call "respondsToSelector:" on the object anyway, what does defining a method as optional actually do for me?
For exmple, let's say I have some code like this
id<MyProtocol> myObj = [[MyClass alloc] init];
if([myObj respondsToSelector:@selector(aMethod)]){
[myObject a开发者_Python百科Method];
}
As long as "MyClass" implements "aMethod", won't this code run exactly the same wether or not MyProtocol defines "aMethod"?
I can see the use of defining this optional protocol purely from a code readability standpoint, but don't understand if it actually has any effect from a technical standpoint (other than making it unnecessary to declare the method in the header).
Pretty much what it says on the tin: it relates to optional functionality. If your aMethod
contains essential functionality in order for your application to work it should be @required
. Otherwise if it provides additional functionality for the implementer to do other things, but its absence won't negatively affect the way it should work (i.e. it's alright that the implementer doesn't respond to the @selector(aMethod)
selector), you can make it @optional
.
You see this a lot in view delegate protocols. Take iOS's UITableViewDelegate
for example: here is a set of the delegate protocol's methods that define views for a table view's section headers and footers:
tableView:viewForHeaderInSection:
tableView:viewForFooterInSection:
tableView:heightForHeaderInSection:
tableView:heightForFooterInSection:
If these are not implemented by the delegate, UIKit simply draws the default section headers and footers for the given tableView
, that come pre-packaged with the default UITableView
element. However, if they are implemented, UIKit uses the custom section header and footer views that are supplied by these methods with the tableView
.
The @optional
keyword pretty much tells the person writing a class to implement the delegate that these methods are optional. I believe UIKit does conformsToProtocol:
and respondsToSelector:
checks internally anyway.
The @optional
/@required
are not just for people - though that would be a good reason itself! - it is also for machines. The compiler, static analyzer, etc. can use them to determine whether a class implementing a @protocol
has provided all the required methods, and determine which optional methods are provided.
Also see Apple's Communicating with Objects, which discusses delegates, protocols, and selectors. Though its listed under Mac OS X, most (if not all) appears to apply to iOS also.
There is also another point to it -- before you can send a message with a particular name, a method with that name must be declared somewhere visible to the scope where it is used. (It doesn't have to be declared as a method of the particular type you are sending the message to -- that's a separate issue, of dynamic typing and static type checking. But it must be declared somewhere as a method to some class or protocol, even if you don't use that class or protocol at all.) This is due to the fact that the compiler must translate a message call to objc_msgSend
or objc_msgSend_stret
or objc_msgSend_fpret
depending on the return type of the method; so the compiler must know the signature of the method.
So an optional protocol method serves that purpose of declaring the method; whereas if you didn't include that method declaration in the protocol, a caller might not be able to call that method, due to there not having been a declaration for it.
Protocols are only there for documentation and compile time checking. Once the program is running, the runtime does not know or care about what static types your objects have (excepting the case where the method returns a struct as mentioned by newacct).
You can do without having protocols at all except that then there is no compile time checking that a particular object has the methods required of it when passed to a particular API. You can declare all your objects as type id
if you like, but that effectively turns off the compiler checking that any messages you send to them are implemented by the objects. It also prevents you from using dot notation for properties.
Given that you have got protocols, once you declare an object as conforming to a protocol e.g.
id<MyProtocol> foo;
That immediately turns the method checks back on. Without optional methods this means that
if ([foo respondsToSelector: @selector(myOptionalSelector)])
{
[foo myOptionalSelector];
}
would flag a compiler warning. Putting the @optional method in suppresses that warning as well as stopping the compiler from having to guess about the return and parameter types.
I think the big difference is the way to call the method!
If you don't define a protocol, you need to use the method performSelector:
to call an unknown method. It works, but in this case, we are limited with the number and the type of parameters of your method. Indeed, you can't give a non object parameter and you could pass no more than 2 parameters with performSelector:withObject:withObject:
. If you want more arguments, you need use an array or a dictionary.
id<MyProtocol> myObj = [[MyClass alloc] init];
if([myObj respondsToSelector:@selector(aMethod:)]){
NSArray *args = [NSArray arrayWithObjects:@"First arg",[NSNumber numberWithInt:2],[NSNumber numberWithBool:YES],nil];
[myObject performSelector:(@selector(aMethod:) withObject:args];
}
Otherwise, if you define a protocol with your optional method you just call it normally:
id<MyProtocol> myObj = [[MyClass alloc] init];
if([myObj respondsToSelector:@selector(aMethodWithFirstArg:second:third:)]){
[myObject aMethodWithFirstArg:@"First arg" second:2 third:YES];
}
Same result, but i prefer the second one with the protocol!
Morgancodes,
You do NOT have to call respondsToSelector
for every method declared in the protocol if you use protocols for type checking as well. A simple explanation can be found Here
Frank
精彩评论