Workaround for: Use of undeclared identifier '_cmd' - objective-c

I have a special NSLog method swizzle that prints the current method as part of the log statement.
For Objective-C methods, I have a macro that uses: NSStringFromSelector(_cmd)
For non Objective-C methods, I have a second macro that uses __PRETTY_FUNCTION__ because _cmd is not defined.
I am now trying to use the same macro for both, so I am trying to test if it is defined and thought I could do so like this:
NSString *command = (&_cmd != nil ? NSStringFromSelector(_cmd) : [NSString stringWithCString:__PRETTY_FUNCTION__ encoding:NSUTF8StringEncoding])
but I am still getting some Use of undeclared identifier '_cmd' errors when the macro is used in non Objective-C methods. How can I do a runtime test do determine if _cmd is defined or not?

You cannot test at runtime whether _cmd is defined.
The __PRETTY_FUNCTION__ macro is defined in Objective-C methods. Just use __PRETTY_FUNCTION__ everywhere and don't try to use _cmd.
Example:
- (IBAction)mapWasTapped:(UITapGestureRecognizer *)sender {
NSLog(#"%s", __PRETTY_FUNCTION__);
}
Here's the output:
2012-12-01 11:45:30.036 TestApp[92581:c07] -[MainViewController mapWasTapped:]

Related

Objective-C method parameter type-safety

If I have methods like:
- (BOOL)isValidRow:(NSDictionary*)contentVersionRow
do we really have to continually check like this at the beginning of the method
if(![contentVersionRow isKindOfClass:[NSDictionary class]]) {
// Handle unusual situation - probably return NO in this case
}
to really implement proper type-safety inside Objective-C methods? Because in theory the parameter is not guaranteed to point to an NSDictionary object, is this correct?
EDIT: So answers so far seem to indicate we should not check for this, but then what is the difference between checking for this and checking for nil parameter, which I assume we should do? Or should we not check for nil either, if it's not normally expected? Both cases cover the situation of a misbehaving caller.
Just like in C you are dealing with pointers in Objective-C. So saying NSDictionary * simply means "here's a pointer to a memory address that contains an instance of NSDictionary".
Example:
#import <Foundation/Foundation.h>
#interface Test : NSObject
- (void)useDictionary:(NSDictionary *)dictionary;
#end
#implementation Test
- (void)useDictionary:(NSDictionary *)dictionary
{
NSLog(#"Keys: %#", [dictionary allKeys]);
}
#end
int main(int argc, char *argv[]) {
#autoreleasepool {
Test *test = [[Test alloc] init];
// 1: This works fine
[test useDictionary:#{#"key": #"value"}];
// 2: This will cause a compiler warning (or error depending on the options passed to the compiler)
[test useDictionary:#"not a dictionary"];
// 3: This will compile without any warnings
[test useDictionary:(NSDictionary *)#"not a dictionary"];
}
}
The 2nd and 3rd examples will cause the program to crash at runtime. So if you want to pass incorrect things to methods, you can. Usually Xcode will warn you if you have a type-mismatch.
Update about nil-checking: If it's an API-misuse to pass nil to your method, then throw an exception. That's what exceptions are for in Objective-C: to catch programming mistakes, not to handle expected runtime issues (like an unreachable network). If your method can just silently fail if nil is passed in or handle it in a sensible way, then do that instead. For example if you have a method addValue:(NSNumber *)number that adds the given value to a sum then it wouldn't be a big deal if someone called it with nil: Just don't add anything :)
Yes, but you shouldn’t.
Obj-C is a dynamic language, so it is up to each object to determine if it responds to a certain method. It is bad style to check the class of an object.
Instead, if you want to check that an object supports a selector you should use -respondsToSelector:, but only if you handle objects not responding to that selector.

Show warnings or errors when redeclaring a variable

It seems that the default behavior in XCode is to silently allow redefinition of local variables if they are declared in a deeper scope, but throw an error or warning otherwise. For example, XCode produces an error for "Redefinition of 'var'" if it is redefined in the exact same scope:
- (void) doStuff
{
NSString *var = #"Hello World";
NSString *var = #"Goodbye"; // Error on this line
}
Similarly, if I have an ivar called 'var', and I try to re-declare 'var' in a local method, XCode will produce a warning for "Local declaration of 'var' hides instance variable" when I try to use it:
//MyClass.h
...
#interface MyClass : NSObject
{
NSString *var;
}
...
//MyClass.m
...
- (void) doStuff
{
NSString *var = #"Hello World";
NSLog(#"%#",var); // Warning thrown on this line
}
So far this is what I would expect. However, if var is redefined in a deeper scope, such as an if block or for loop, XCode allows it, and the outer declaration is silently ignored:
NSString *var = #"Hello World";
if (TRUE)
{
int var = 0;
NSLog(#"%d",var); //prints '0', No errors or warnings
}
NSLog(#"%#",var); //prints 'Hello World'
Why is the last example silently allowed, but the other two are caught? Is there some option or flag I can toggle in XCode so that an error or warning would also be created in the last example? If XCode won't catch it for me, is there some code I could write to make sure variables are never redefined? Or is it just my responsibility to make sure I'm not re-using my variable names?
In the build settings (Xcode 5 & 6, at least) you can set a warning for Hidden Local Variables to YES.
The last example is behavior that Objective-C inherits from standard C. A variable's scope is determined by the bracing level. It's been that way since the earliest days for C. It's called variable shadowing, and it's actually pretty useful in ensuring that code keeps working even in the face of API changes in system libraries.
As far as why it's allowed, but the earlier examples aren't, that's a consequence of how Objective-C implements instance variables. The instance variables are essentially treated as local variables of each of the class's methods. So when you declare a local variable in a function that shadows an instance variable, it gets flagged as an error. Basically the first and second cases are treated as equivalent.
To get a warning for these cases, set the LLVM warning option Hidden Local variables to Yes.

Obj-C: Difference between calling a method (with no input) on an object vs calling a method with input

I am an absolute beginner in objective-c and just read an overview of the language on cocoadevcentral.
The first section briefly discusses syntax for calling methods and gives two examples; one for calling a method on an object and the second is a method with input being called on an object,
[object method];
[object methodWithInput: input];
Can anyone explain the difference to me, possibly with a simple example?
There is no huge difference between the two and all depends on what you are doing.
Method 1
[object method];
There are two parts to this method.
object this is either an instance of a class or is a class itself all depends on the type of method you are calling whether it be an instance method or a class method. Each are declared differently.
A Class method is declared like + (void)myClassMethod; and would be called like [NSString myClassMethod];
An Instance method would be declared like - (void)myInstanceMethod; and would be called like [myStr myInstanceMethod]; (Where myStr is an instace of NSString)
method The second part is the actual method that you are calling this all that this will do when you call something like [myStr myInstanceMethod]; it will call the implementation of that method so it would call
- (void)myInstanceMethod
{
NSLog(#"We called our instance method");
}
Method 2
[object methodWithInput: input];
The only difference here is that we are passing in an argument. So here we have three parts the same first two from method 1 and the argument
input All this is, is the value that you are passing into the method to be used within it.
This type of method would be declared something like - (void)myInstanceMethodWithArgument:(NSString *)str;. Here are just saying that we have an argument of type NSString so when we call this like [str myInstanceMethod:#"Some Random String I want to pass in"]; it will run the following implementation code
- (void)myInstanceMethod:(NSString *)str
{
NSLog(#"My str value is : %#", str);
}
Method 3
[object methodWithInput1:input1 andInput2:input2];
Just throwing this in because you my get a little confused later when dealing with multiple arguments. This is exactly the same as method 2 except it has two arguments and not one. This would be declared like - (void)myInstanceMethodWithInput1:(NSString *)str1 andInput2:(NSString *)str2;. Does exactly the same is method 2 except it has multiple arguments that's it nothing to be scared of.
I would recommend that you have a read of the Apple Coding Guidelines for Cocoa. Best of look with learning as it's probably not the easiest language to learn.
Try substituting 'input' for 'argument'..
[object someMethod:(CGFloat )floatArgument];
The type should be there in the brackets, with a dereference operator (*) eg (NSObject *)theArgument if that argument is a pointer.
So basically some methods supply one or more arguments, and some do not, just as with C
When you call method without input data it means that method will work with already existing class's properties.
- (void)someMethod {
self.var_1 = self.var_2 + self.var_3; //or any other implementation
}
You will call this method like this
[self someMethod];
When you call method with some input data it means that this data will be used in method's implementation
- (void)someMethodWithInputData:(NSInteger)inputData {
self.var_1 = self.var_2 * inputData;
}
You will call it like this
[self someMethodWithInputData:10];
It's just the difference between saying "I wait" and "I eat an omelette". In some cases you can say what you mean with just a verb. In some cases a sentence needs an object in order to communicate its meaning.
The same thing applies in programming. Sonetimes you're going to need to specify more than just the action. But not always.

incompatible pointer type

I have this class:
#interface G2Matrix : NSObject
...
- (id) initWithArray:(float *)val;
...
#end
This line below give me a warning saying that the first argument to the method initWithArray has an incompatible pointer type:
float m[16];
...
G2Matrix* matrix = [[[G2Matrix alloc] initWithArray:m] autorelease];
If I change the method name to something like initWithArray1 the warning disappears. I know that some objects in foundation classes have a method with the same name, but I am deriving from NSObject, which doesn't have this method. What gives?
Additional info - I call the same initWithArray method from other init methods in the G2Matrix class, but I don't see the warning there.
At a guess, this is a type problem:
Inside the other init methods, you call [self initWithArray:...]. self is typed as a G2Matrix*. In this context the compiler can fully resolve which imp (C function pointer) will eventually handle the method call, and detect its signature (argument and return types) correctly.
Out in regular code, [G2Matrix alloc] returns an id. In this context the compiler can only tell the method selector, which will be bound to an imp at runtime. It has to guess which initWithArray: you mean, and as you can see from the warning it guesses wrong, since a foundation class has an initWithArray: method with a different signature. Your code does still work, the compiler just can't be certain.
Picking a unique name for the initMethod (initWithFloats: maybe?) is the recommended way to shut the warning up. Other ways are: break it into two lines; or cast the alloc return value to the right class:
G2Matrix *matrix = [G2Matrix alloc];
matrix = [[matrix initWithArray:pointerToFloats] autorelease];
// or
G2Matrix* matrix = [[(G2Matrix *)[G2Matrix alloc] initWithArray:m] autorelease];
Looks a little odd, but allows you to turn the treat-warnings-as-errors compiler flag back on.
#tathagata thats because initWithArray is method defined in NSArray class so you cannot use it unless you subclass NSArray class.
see the documentation on NSArray
http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSArray_Class/NSArray.html
PS.
by use the method, i meant Override the existing method for your purpose which is not a good idea you can find the Subclassing Notes in the document.

Functions in Objective-C

I am trying to write a function which returns a string created from two input strings;
but when I try the function declaration
NSString Do_Something(NSString str1, NSString str2)
{
}
the compiler gets sick. (Worked fine for a different function with int arguments.)
If I change the input arguments to pointers to strings, in also gets sick.
So how do I pass Objective-C objects into a function?
All Objective-C objects being passed to functions must be pointers. Rewriting it like this will fix your compiler error:
NSString *Do_Something(NSString *str1, NSString *str2) { }
Also, please keep in mind that this is a (C-style) function and not an instance method written on an Objective-C object. If you wanted this to actually be a method on an object it would probably look something like this:
NSString *doSomethingWithString1:(NSString *)str1 string2:(NSString *)str2 { }
I say "probably" because you can name it however you want.
Functions are perfectly fine in Objective-C (and in fact earn some of the language's benefits).
See my answer to C function always returns zero to Objective C, where someone was trying what you are and had a problem with the compiler assuming return type. The structure that I set up there is important when you are using functions, just like when you are using objects and methods. Be sure to get your headers right.
To be pedantic, you're using a function definition of:
NSString *DoSomething(NSString *str1, NSString *str2) {
// Drop the _ in the name for style reasons
}
And you should be declaring it in a .h file like so:
NSString *DoSomething(NSString *str1, NSString *str2);
Just like C.
that doesn't work for me. i've just declared in the .h:
NSString *myFunction(NSDecimal *value);
and i type in the .m:
NSString *myFunction(NSDecimal *value){
//code
}
but always i get an error saying expected '(' before '*' token
now is fixed. for some reason... sorry.