Understanding Objective-C scope issue - iphone

Following is a snippet from a View Controller's implementation:
- (void)myOtherAwesomeMethod
{
[self myAwesomeMethod]; // Compile ERROR here: Receiver type for instance message does not declare a method with selector
}
- (void)myAwesomeMethod
{
NSLog(#"%#", #"Calling my awesome method...");
}
- (void)viewDidLoad
{
[self myAwesomeMethod];
[self myOtherAwesomeMethod];
}
I do not have myAwesomeMethod method declared in my header file, but why is it that I can call myAwesomeMethod in viewDidLoad, but not in myOtherAwesomeMethod?
I know the solution to this error is to declare the method in my header file, but I'd like to understand why this is happening.

In C, the rule is: you got to declare it before you use it.
Files are compiled from the top down. So here's what is happening in your code:
The compiler reads the #interface declaration for your class. Since you say neither method is declared in the header file, neither are in the symbol table yet.
Your symbol table contains: nothing yet
The compiler reads the method definition for myAwesomeMethod. You haven't declared it yet, so it gets added to the symbol table. The body of the method contains a call to NSLog, which was declared long ago in a header provided to you by Apple.
Your symbol table contains: myAwesomeMethod
The compiler reads the method definition for viewDidLoad; it was actually declared in a superclass's header file. The body of the method contains a call to myAwesomeMethod, which was found! It also contains a call to myOtherAwesomeMethod. Never heard of it!
Now, this isn't an error, because it can still generate the code to make the call. It can infer argument types by how you use it (in this case, no arguments). It can't be sure of the return type, though (just because you don't use the return value doesn't mean there wasn't one). So it proceeds on the assumption that the call returns id and generates a warning.
Your symbol table contains: myAwesomeMethod
Finally, the compiler reads the method definition for myOtherAwesomeMethod. It gets added to the symbol table. It compiles the body, which contains a call to myAwesomeMethod, which is in the table. All's well that ends well.
At the end of the file, your symbol table contains: myAwesomeMethod, myOtherAwesomeMethod
If you're coming from a language like Java, this may seem silly, but that's because Java works differently than C. In Java, source code is compiled and linked in one step; you can't compile a class that refers to another class without having either the source code or class files available. That can be a hassle, but on the other hand, you never need about declarations apart from definitions.
In C, compiling and linking are different steps. The compiler just generates object code that refers to symbols that may be defined elsewhere; it uses declarations to make sure it generates the right code. The linker is responsible for matching up all those symbols with definitions in other libraries, static or dynamic.
In Objective-C, the linker doesn't actually know/care about Objective-C messages, because there is no way of knowing at compile-time/link-time whether an object can respond to a message. So it passes the buck to the runtime, which throws an exception if you get that far without a method definition.

I admit I'm not sure why it works in viewDidLoad.
Notice that the error says "did not DECLARE" a method. That's a hint that it's looking for a declaration (typically in a header)..ie a function prototype.
However, there's nothing that says a declaration has to be in a header. YOu can put it at the top of the implementation file if you're not going to call that method from outside the file.

Related

Is there a way to throw warnings at the place of calling if the method called has a warning in XCode?

I want to add warnings to some methods that have legacy code in both objective C and Swift , whenever these methods are used somewhere by other developers, I want them to be warned that they are using legacy code, it is not required to be shown as deprecated or obsolete I only need to warn them about it.
Eg. Let this be the function definition
func someLegacyMethod() {
#warning this is legacy code
// some function
}
When a user calls this method I want him to be warned. I thought of using swiftlint but couldn't find a way to do this because the definition is elsewhere and the calling place is elsewhere and this needs to be future proof because these methods can be used in many places in the future.
I am open to exploring other ways to implement this too.
someLegacyMethod() //warning should appear here
Edit1:
I found this:
https://blog.twitter.com/engineering/en_us/a/2014/attribute-directives-in-objective-c
and tried using attribute((warning(…))) but XCode is complaining : unknown attribute 'warning' ignored.

Why do we call doesNotRecognizeSelector: method?

I am working with socket programming.I just wanted to clear a doubt related with a code i downloaded from -mobileorchard.com - Chatty. While R&D , I saw a function call in ChatRoomViewController.m file
[chatRoom broadcastChatMessage:input.text fromUser:[AppConfig getInstance].name];
when I saw in Room.m file, for the implementation of above call; it was
- (void)broadcastChatMessage:(NSString*)message fromUser:(NSString*)name
{
// Crude way to emulate an "abstract" class
[self doesNotRecognizeSelector:_cmd];
}
i googled for "doesNotRecognizeSelector:" , according to Apple its for error handling, stating "The runtime system invokes this method whenever an object receives an aSelector message it can’t respond to or forward." my question is why does the developer call the broadcastChatMessage:fromUser: function if its none of use there and to handle which method's "selector not found" exception ?
According to Stackovrflow, its used to create abstract class , according to this Question, its to avoid "Incomplete implementation" warning.
I am still not getting why that method is used in that Chatty Code, Kindly help me to understand the reason why that method is used.
This is the method that exists on every NSObject derived object that triggers the path to an exception when a method isn't recognized in a runtime call. For example, if you try to send a message to an NSString called -foo, it'll end up there since that's not a valid method on NSString.
In this case, the Chatty class Room is a base class that is never used directly. LocalRoom and RemoteRoom derive from it, and both of those classes provide an overriding implementation of -broadcastChatMessage:fromUser. Nobody ever calls that base class version, but for "completeness" the programmer has guaranteed that a subclasser must override this by implementing the method, but then turning around and calling this to trigger an exception.
Thing is, this isn't specifically idiomatic Objective-C. An "abstract" class is a concept from C++ and other languages; it's base class that exists only as a "pattern" from which to subclass. (In ObjC, this is often done by creating a formal #protocol when there isn't meaningful state, as there (mostly) isn't here).
Note that the call to -doesNotRecognizeSelector: is arbitrary. It's not necessary to avoid compiler warnings here (since the method is in fact implemented) and the original writer could have easily just thrown an exception directly, or done nothing instead.
It seems to me that you already answered your own question. There is no method to make abstract classes in Objective-C, so the closest thing to do it to have the methods that you need to override throw exceptions. If you override this method in a subclass, then doesNotRecognizeSelector: will no longer be called. Basically it is a way to get a developer to promise to implement this method in their subclass.
Also, as you mentioned, if you don't put this in then the compiler will issue a warning because no implementation exists for a method defined in the header. This will perform the same behavior as not implementing it, but the compiler will realize that you are doing it on purpose.

Why is my 'kMyConstant' macro redefined?

In my app delegate class I define a constant like so:
#define kSomeConstant #"My_Constant_Value"
I then want to make use of this constant in another viewController so I defined it again exactly as above. I now get the message:
'kSomeConstant' macro redefined
Why is this the case? Is there some other way I can access the constant?
Simply redefining the constant (ie : same constant name). Choose another name, or define it just one time in a header file to access the constant value every where you import that header.
It's likely been defined twice among the sources which are visible to the compiler. the compiler may tell you where the previous definition is if you look into the message in more detail.
To avoid this problem entirely, declare your NSString constants like this:
file.h - declaration:
extern NSString* const kSomeConstant;
file.m/mm - definition:
NSString* const kSomeConstant = #"My_Constant_Value";
Then when you need to use the constant, just #include "file.h".
The compiler has encountered two definitions of the macro. Probably you defined it in a header file and then defined it again in a .m file.
If you can't figure out where the two definitions come from, try preprocessing the file with the error (in Xcode 4.2 this is under generate output in the product menu). You can then do a search for kSomeConstant and that should tell you where it comes from.

Error: 'class name' redeclared as different kind of symbol?

I was facing the same error as asked in this question
I overcome with this error by solution of declaring class ahead of time in my .h file with the class parameter
I am having FFTBufferManager.h and FFTBufferManager.cpp file and using it in HomeView.h and HomeView.mm file
class FFTBufferManager,CAStreamBasicDescription,DCRejectionFilter;
But now I am having error as
#include "FFTBufferManager.h"
#include "aurio_helper.h"
#include "CAStreamBasicDescription.h"
class CAStreamBasicDescription,FFTBufferManager; //here it shows this error
EXpected Unqualified-id befor ',' token
#interface HomeView
{
FFTBufferManager* fftBufferManager;
//it shows erros
EXpected Unqualified-id befor ',' token
ISO c++ forbids declaration of FFTBufferManager with no type
}
#property FFTBufferManager* fftBufferManager;
//shows error
'FFTBufferManager' is not a type
I'm gathering you're using both C++ and Objective-C.
I'd suggest renaming all your .cpp and .m files in which Objective-C and C++ code are meeting to use the extension .mm - this tells the compiler to use "Objective-C++" rules, and will stop a lot of compiler troubles.
Also, it seems CAStreamBasicDescritpion is a C++ class - you'll have to forward-declare it with class CAStreamBasicDescritpion;, not #class CAStreamBasicDescritpion; (note, no "at" sign) - the second form is only for forward-declaring Objective-C classes. This I suspect is the root cause of the particular error you have observed.
EDIT in response to comment: I'm not sure about your first new issue - that should work fine so long as both FFTBufferManager and CAStreamBasicDescription are C++ classes. As to your second one, depending on where exactly that line of code is (CAStreamBasicDescription thruFormat;) you may need to include the header rather than just the forward-declare: you're declaring an instance of CAStreamBasicDescription here, and the compiler needs to know its structure to do so.
You can't declare more than one class at a time.
Change your declarations to
class CAStreamBasicDescription;
class FFTBufferManager;
The compiler is looking for an unqualified-id because it believes that you're declaring a variable of type CAStreamBasicDescription, so it expects a variable name where you gave it a comma.
Looks like you are trying to create a class that already exists in one of the Cocoa frameworks.

Must I import my category header file every time I want to use that additions?

For some reason the compiler gives me a warning that my category methods may not be there (like "foobar may not respond to doFoo"). However, the category works.
Yes, you must import your category header file. Otherwise, the compiler will give a warning for any calls to methods defined in the category. This is exactly the same as when you call a method on a class, and the method is not defined.
Because of Objective-C's dynamic nature, at runtime it will still be able to find and execute the method you call, despite the compiler warning. That is why the warning is worded "foobar may not respond to doFoo". At compile time the compiler cannot find a declaration of the doFoo method, therefore it may not be able to call the doFoo method correctly, however at runtime the doFoo method may be called correctly.