I have just started to develop for the iPhone and am in the process of learning Objective-C. I have seen some code that implements a method in the #implementation side of a class like this:
-(void)myMethod; {
// method body
}
What makes this interesting is that there is no mention of myMethod in the #interface for the class. I tried a sample project with this and when I compile I get a warning from XCode that myMethod may not be seen by the calling code.
Can anyone tell me what is going on?
Thanks!
It's just like functions in C. You don't need a declaration (i.e. it doesn't have to be in the #interface) but if there's no declaration, any code before the method definition will generate that warning. Code after the method definition will not generate a warning.
In ObjC, method calls are resolved dynamically (dynamic binding), meaning that when you do [obj myMethod];, internally the ObjC runtime software will go through the class methods at that point in time and if it finds one called "myMethod" it will then call it.
Also it is possible to add methods to an object at runtime.
The method declarations in an #interface section is only there to help the compiler determine what methods are publicly available for a given class. If you do add a method in your #implementation only, the compiler may warn you about that, but the code will still compile and work.
I sometimes use this to add internal methods to my objects, which are only called from other methods after it, and never from outside. Though I don't remember seeing warnings about it... Make sure that the calling code is placed after the method implementation in the same file.
Related
Assume that:
New Protocol is declared
Method in this protocol is marked #required
Class conforms to Protocol
Class does not implement the method mentioned in Protocol
At compile time, information about this method is known: i.e. that it is required and that this class and any other classes this class may may extend do not implement it.
Why in this case the compiler issues a warning and not an error?
Errors are only issued when the compiler cannot continue because something went terribly wrong.
When calling a method in Objective-C, the method lookup is done during runtime and not during compilation, which C++ does. In Objective-C a "message" is simply sent to the object, something like obj.executeCommand("Hey, can you execute function <name> for me?"). In C++ the object will be called directly, in a way like obj.<name>(). In the case of Objective-C the executeCommand() method is called, which exists. In C++'s case the function is called but it does not exist. These are methods that are linked on the compiler level, which means they both become memory addresses rather than names. executeCommand becomes 0x12345678 but it still uses the same message ("execute function <name>").
This is probably very confusing, but it's related to the way methods are implemented in different languages.
If you feel strongly about it, why not turn on -Werror?
I don't know the real answer but here is a use case that would go against it.
What if you implemented all of the protocol methods in a category???
Main interface declaration adopts the protocol however the protocol method implementation is in a category.
This is valid code but will show compile error if compiler was that strict !!
Objective-C is a dynamic language. The idea of what an implementation is, is different to a static language.
For the most part, it's in code that most of us implement inside the #implementation ... #end block.
But what if a method is not found? Then an object has a chance deal with it dynamically.
Imagine you have an interface for a sound effect player:
#protocol FX
- (void)playBeep;
- (void)playSiren;
- (void)playHonk;
#end
An implementation could have the files Beep.mp3, Siren.mp3, Honk.mp3 to play, but instead of implementing each of the methods, it could override -forwardInvocation: and parse the selector string, something like this pseudocode:
NSString *selName = NSStringFromSelector([invocation selector]);
if ([selName startsWith:#"play"]) {
NSString filename = fileNameFromSelector(selName);
[self playSoundFileNamed:filename];
}
This may seem contrived, but once you start using the dynamic features of the language, you will start finding more and more places where it makes sense. And by sense I mean, does this effort help in the long run?
In the above case, just add a -sound* method name to the interface, and drop in a appropriately named sound file. It just works.
Another example from personal experiments: how to deal with Core Data entities in a more natural way. I want to do this:
NSArray *people = [Person findAllWithNameLike:#"B%"];
instead of mucking about with predicates, fetch requests etc.
But I don't want to define every permutation of method in code.
How about if I wanted to build an XML builder? I would look at a dynamic approach. It has served Groovy Builders well (look at Groovy/Grails for examples).
One last example: I have a traits system where I can define behaviours in the form of groups of methods and have my objects assimilate this behaviour. So, while the compiler doesn't see an implementation for the interface my object conforms to, the implementation is injected into it from a trait class, using the Objective-C runtime. Why would I do this? I find many delegate methods are boiler plate, but at the same time, a single base class for each situation is not flexible enough. Instead of cut and paste from code samples, my 'samples' compile and run :) and any changes are reflected across all projects using the trait.
To really understand why all this is available to you, it is worth playing around with a Smalltalk environment (search Pharo or Squeak). This is where Objective-C has its roots.
And finally, to stop these warnings:
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wprotocol"
#implementation ... #end
#pragma clang diagnostic pop
Because there are times when there are bogus "required" methods in a poorly designed protocol.
They should have been optional but someone insisted they are "required".
Thusly making this a run time issue rather than a compile bug is very very wise.
I want to go to there. Seriously though, how does one implement a pure virtual method in an "Apple" way? Do you use a Protocol with your base class and throw exceptions on those methods?
When you program in Objective-C you need to purge your mind of such things as virtual methods. You don't call methods on Objective-C objects, you send messages to them. Objects either respond to messages or they don't, but due to the dynamic binding, you can't tell this until run time.
Thus, you can declare a method on a base object and not not provide an implementation, no problem (except for the compiler warning), but you can't have the compiler flag up when you directly instantiate an object with such methods and it won't throw an error at runtime unless you actually send that message to the object.
The best way to create "virtual" base classes (in my opinion) is to declare the method and give it a stub implementation that throws a suitable exception.
In Objective-C, there is no pure virtual support as in C++.
A simulation would be that you declare a method in your interface but don't implement it in your .m file. Of course you'd get compiler warnings but IIRC you can turn those off. But you won't get warnings/errors if you don't overwrite them in the subclass, which you get in C++ (IIRC).
An alternative would be to implement them with just an NSAssert(NO, #"Subclasses need to overwrite this method"); body. Still, you'd only catch this at runtime, not compiletime.
Depending on what you're doing the delegate pattern may be more appropriate than a subclass, where the delegate is defined as id<YourDelegateProtocol>. The compiler will generate a warning if the required methods in the delegate protocol are not implemented.
Subclassing is generally avoided in Objective-C since objects cannot inherit from multiple superclasses but they can implement multiple protocols.
You should use the:
- (void)doesNotRecognizeSelector:(SEL)aSelector method.
As noted by Apple, here: https://developer.apple.com/library/mac/#documentation/cocoa/reference/Foundation/Classes/NSObject_Class/Reference/Reference.html
You have a few options, but you're on the right track.
ObjC doesn't support this directly, forcing subclasses to implement a protocol is the best way to check it at compilation.
'Secretly' implementing the method in the base class and asserting is what I do to confirm the subclasser has subclassed correctly at runtime. Some people have mixed feelings about assertions, or must leave them active, so that's not always a good solution.
You can also force subclasses use a specific class constructor and initialization sequence, then verify they have implemented everything required before returning an instance, in case compiler warnings don't cut it.
But ObjC is missing some lang features which allow clients to shoot themselves in the foot, or workaround what they wish so... you shouldn't get too stuck on enforcing it.
note: Exceptions are very uncommon (and a bit unsafe, too) in ObjC.
A virtual method is a method whose behavior can be overridden within an inheriting class by a function with the same signature (i.e same name with same number of params and type of params).
Example:-
#implementation BaseClass
-(void)viewDidLoad
{
[self virtualMethod:123];
}
-(void)virtualMethod:(int)param
{
//implement this method in subclass
}
#end
////////////////////////////////////////////////////
#interface ChildClass:BaseClass
#end
#implementation ChildClass
-(void)virtualMethod:(int)param
{
NSLog(#"There is no keyword "Virtual" in Objective C.");
}
#end
Output:-
"There is no keyword "Virtual" in Objective C."
This is not an actual Xcode error message, it is a warning that has been haunting me for
a long time. I have found no way of removing it and I think I maybe have overstepped some unwritten naming convention rule.
If I build a class, most often extending NSObject, whose only purpose is to do some task and report back when it has data, I often give it a convenience constructor like "initWithDelegate".
The first time I did this in my current project was for a class called ISWebservice which has a protocol like this:
#protocol ISWebserviceDelegate
#optional
- (void) serviceFailed:(NSError*) error;
- (void) serviceSuccess:(NSArray*) data;
#required
#end
Declared in my ISWebservice.h interface, right below my import statements.
I have other classes that uses a convenience constructor named "initWithDelegate".
E.g. "InternetConnectionLost.h", this class does not however have its methods as optional, there are no #optional #required tags in the declaration, i.e. they are all required.
Now my warning pops up every time I instantiate one of these Classes with convenience constructors written later than the ISWebservice, so when utilizing the "InternetConnectionLost" class, even though the entire Class owning the "InternetConnectionLost" object has nothing to do with the "ISWebservice" Class, no imports, methods being called, no nothing, the warning goes: 'ClassOwningInternetConnectionLost' does not implement the 'ISWebserviceDelegate' protocol
I does not break anything, crash at runtime or do me any harm, but it has begun to bug me as I near release. Also, because several classes use the "initWithDelegate" constructor naming, I have 18 of these warnings in my build results and I am getting uncertain if I did something wrong, being fairly new at this language.
Hope someone can shed a little light on this warning, thank you:)
The cause here is that you have multiple interfaces in your project that have functions defined as initWithDelegate:(id).
Alloc only returns id, so the compiler doesn't know the type, it therefore doesn't know which initWithDelegate is being used and seemingly uses the first one in the list. Thus you get a warning that makes no sense.
The runtime knows the type of the object however, and thus doesn't have any trouble.
The solution seemingly is to use more verbose init methods, eg. initWithWebServiceDelegate. Or to cast after the alloc:
[(Foo *)[Foo alloc] initWithDelegate:self]
Your getting the warning because the compiler thinks that the classes have declared that they implement the ISWebserviceDelegate protocol. I doubt it has anything to do with the intiWithDelegate method unless your inheriting the method or you've defined the method in a protocol that itself is an extension of ISWebserviceDelegate protocol.
The usual cause of this is declaring the adoption of the protocol in a superclass and then forgetting to implement in that superclass of a subsequent subclass.
If you're sure that is not the case, then you probably just need to clean the project and rebuild. You most likely did declare at one point, then undeclared but the compiler is stuck on the old declaration.
If the clean does not resolve, I would do a project find on ISWebserviceDelegate and make sure its not tucked away somewhere.
I have found that very often, when calling myMethod asoociated with myObject1 from myObject2, I get the warning that "myObject1 may not respond to -myMethod", but then the program runs just fine anyway. Why doesn't the compiler recognize declared methods at compile time?
John Doner
This shows up as a warning because Objective-C is a very dynamic language. At compile time, the compiler hasn't found the declaration for "myMethod" (perhaps you're missing a header file, or forgot to include it in the header?) when it was attempting to compile the file. However, it only generated a warning because Objective-C has the ability to create and load extra methods at runtime so that by the time that code executes, that method will exist. Hence, it is only a warning.
Most likely you just haven't declare the method in the appropriate header file.
The warning means you're calling a method for which the compiler has not yet seen a method declaration. It is an error in most other languages, and it is certainly a warning you cannot ignore.
If you haven't declared the method, do so in an #interface block at the top of your source file (if it's a private method) or in your class's header file (if it's a public method).
If you have declared the method in a header file, be sure to import the header file.
If you have declared the method and you are importing the correct header file, you have a typo somewhere.
One case where this happens frequently is if the type of the variable that contains the object is that of a superclass, and the method is only defined for the subclass. You can avoid this by either typing it as id or making the static typing more specific. If the variable type is that of the class itself, it's likely that the method isn't visible to the compiler in the scope where you're trying to invoke it — the other answers deal with this situation.
Or sometimes, if you're using a delegate class, you need to define a category with those delegate methods in order for the compiler to find them.
Usually, adding
#class myObject1
will solve the problem. Check out Ben Gottlieb's answer to Objective-C #class vs. #import here on Stack Overflow.
I'm reading the source code of an open-source project and I've encountered the following category definition in an implementation file:
#interface QCView (Private)
- (void)_pause;
- (void)setClearsBackground:(BOOL)flag;
#end
At first I thought that the setClearsBackground method was being added to the QCView class definition. But when I search through this implementation file, I find no implementation of a setClearsBackground method (although this message is sent to a QCView instance in a few places within the file).
Why would someone declare a method on a framework class like QCView but then not implement that method anywhere? My only guess is that this is a way to circumvent the compiler and call a method that isn't declared in the QCView.h file. But this seems unlikely, because how would the developer know that an implementation for this method even exists?
The developer probably used a tool such as class-dump in order to generate headers which list all the methods implemented by a framework. This is useful if you need to access SPI and have no other choice. Without the declaration, Objective-C makes assumptions about the method parameters and, beyond generating a warning, may generate an incorrect method invocation.
All the usual caveats about an undocumented interface breaking in a future OS revision apply. At the minimum, you should check if the object responds to that method (-[NSObject respondsToSelector]). For extra paranoia you can wrap the invocation in an exception block, in case the method remained but its behavior changed.