Below is a simple PerformSelector that sends a message to obj to perform the looping method. All works well but I get the following yellow warning.
PerformSelector may cause a leak because its selector is unknown.
#import "MyClass.h"
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[])
{
#autoreleasepool {
MyClass *obj = [[MyClass alloc]init];
SEL mySel = #selector(looping);
[obj performSelector:mySel];
}
return 0;
}
This warning does not make sense because the performSelector must be aware of mySel because the looping method does get called - any ideas whats going on ??
Update
MyClass.h
#import <Foundation/Foundation.h>
#interface MyClass : NSObject
-(void)looping;
#end
MyClass.m
#import "MyClass.h"
#implementation MyClass
-(void)looping{
NSLog(#"Hey, i'm looping");
}
#end
Update -- The Real Answer
This is ARC specific:
performSelector may cause a leak because its selector is unknown
In short, ARC uses information based on the naming conventions and any additional attributes bound to the selector. When accessing a selector by name and performing it via the performSelector: family of methods, that information is lost and the compiler is warning you that it must make some assumptions regarding reference counting because this information is stripped away.
In short, the specific program you posted is safe but you are encouraged to use an alternative which is ARC-friendly.
The Previous Response
A selector's declaration does not need to be visible to the current translation in order to call it.
The compiler is allowed to assume default types for parameters and return types for class and instance methods (id is the default).
There are several compiler warnings which can warn you of these shady actions.
You probably forgot to declare the selector looping in the #interface, or you may have omitted the colon, if it has arguments: looping: would be its name.
this warning is due to that u have not told the compiler where the selector resides, import the file where it is or add the selector to header file where it should be
Related
Here is a code snippet from Learning objective-c 2.0
Full code:
ClassWithFloat.h
#import <Foundation/Foundation.h>
#interface ClassWithFloat : NSObject
{
float value;
}
-(void)setValue:(float)aValue;
#end
ClassWithFloat.m
#import "ClassWithFloat.h"
#implementation ClassWithFloat
-(void)setValue:(float)aValue
{
value = aValue;
}
#end
ClassWithInt.h
#import <Foundation/Foundation.h>
#interface ClassWithInt : NSObject
{
int value;
}
-(void)setValue:(int)aValue;
#end
ClassWithInt.m
#import "ClassWithInt.h"
#implementation ClassWithInt
-(void)setValue:(int)aValue
{
value = aValue;
}
#end
main.m:
#import <Foundation/Foundation.h>
#import "ClassWithFloat.h"
#import "ClassWithInt.h"
int main(int argc, const char * argv[])
{
#autoreleasepool {
id number = [[ClassWithInt alloc] init];
[number setValue:3];
}
return 0;
}
failed to compile, after changing to ClassWithInt* number it works.
Error message:
/Users/jcyangzh/src/oc/SameName/SameName/main.m:17:9: Multiple methods named 'setValue:' found with mismatched result, parameter type or attributes
But since objective-c is somehow a dynamic programming language, the message call will be translated to native C method call.
obj_msgSend(number, #selector(setValue:), 3)
the obj_msgSend method find the class structure for the number object by isa variable. Which should make no difference between id or ClassWithInt type.
Why objective-c compiler could not recognize the right method?
Note: I am asking this question, because having same method name, but different argument type for different class is reasonable to me. But it seems that it is not possible either because the compiler limitation or the language design (do not supporting method overloading etc).
The problem really is that your object is only typed as id within the lexical scope.
The compiler doesn't know which method of the same name/selector to use.
You have multiple classes that have that selector but with different signatures because their arguments are different types.
You should avoid id in this case
Or typecast your object in the message send brackets to tell the compiler what class's method to use
Or
Bracket the same message call repeatedly in a sequence of if ([obj isKindOf:
checks. (Crazy here)
Or
Best take a hint from NSNumber class on good method naming conventions and do something like setFloatValue: and setIntValue: which is more readable and clear and helps the compiler a bit.
But any time you have and id type only, you need to be checking if the object isKindOf: or you are asking for trouble.
It is very very bad to have methods with same name but different signatures. (It is documented somewhere but I can't find now)
The calling conversion between calling setValue:(float) is different to setValue:(int), compiler have to generate different binary code.
As you said, it end up with something like
obj_msgSend(number, #selector(setValue:), 3)
but they are different
obj_msgSend(number, #selector(setValue:), (int)3)
obj_msgSend(number, #selector(setValue:), (float)3.0f)
Compiler have to decide at compile-time to generate the which version. Because the calling conversion between pass parameter with int type and float type are different.
Given code
ClassWithInt *number = [[ClassWithInt alloc] init];
[number setValue:3];
Compile know it need to generate the version with int with the help of type information.
but without type information
id number = [[ClassWithInt alloc] init];
[number setValue:3]; // is this takes int or float? if it is float then 3 need to be convert to float value first
There are two possible way to call it. Compiler can't figure it out without help. Hence the error message.
In this tutorial here: http://www.raywenderlich.com/62989/introduction-c-ios-developers-part-1
It mentions that for Objective-C:
Even if you only declare a method inside the implementation of a
class, and don’t expose it in the interface, you technically could
still call that method externally.
How is this done?
There are a lot of ways.
For example, as long as a compatible method is declared somewhere, you can call it normally with dynamic typing. Here's a demonstration:
// MyClass.h
#interface MyClass : NSObject
#end
// MyClass.m
#interface MyClass()
- (void)addObject;
#end
#implementation MyClass
- (void)addObject:(id)object {
NSLog(#"Whoa, I got called!");
}
#end
// main.m
#import <Foundation/Foundation.h>
#import "MyClass.h"
int main() {
id something = [[MyClass alloc] init];
[something addObject:#"Look ma, no errors!"];
return 0;
}
Since there is a known method named addObject: that takes an object, and id variables are dynamically typed, this is 100% valid and will call MyClass's addObject: method.
They could even get it with a statically typed variable and a method that isn't known by declaring the method in a category. A few other options:
using performSelector: as #michaels showed in his answer
going straight to objc_msgSend()
getting the method IMP and calling it directly.
You can use the performSelector: method of NSObject, though the compiler will give you a warning if the selector is not publicly declared anywhere
[someObject performSelector:#selector(someMethod)];
I am trying to create a custom object that simply inherits the NSString class and overrides the 'description' method.
When I compile, however, I am getting a warning:
Incompatible pointer types initializing 'OverrideTester *' with an expression of type 'NSString *'
Here is my code:
main.m
#import <Foundation/NSObject.h>
#import <Foundation/NSString.h>
#import <Foundation/NSAutoreleasePool.h>
#import "OverrideTester.h"
int main (int argc, char *argv[])
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSString *str = #"Programming is fun";
OverrideTester *strOverride = #"Overriding is fun";
NSLog (#"%#", str);
NSLog (#"%#", strOverride);
[pool drain];
return 0;
}
OverrideTester.h
#import <Foundation/Foundation.h>
#interface OverrideTester : NSString
-(void) description;
#end
OverrideTester.m
#import "OverrideTester.h"
#implementation OverrideTester
-(void) description
{
NSLog(#"DESCRIPTION!\n");
}
#end
NSString is part of a class cluster. You cannot just create arbitrary subclasses of it, and when you do, you can't assign constant strings to them (which are type NXConstantString). See Subclassing Notes in the NSString documentation. Generally you don't want to subclass NSString. There are better solutions for most problems.
you are assigning an instance of NSString to your variable of type OverrideTester. If you want an instance of your class, you need to instantiate an instance of that class; type-casting will never change the class of an instance.
description is defined as returning an NSString*:
- (NSString *)description;
Do not try to learn about subclassing and overriding methods by subclassing NSString (or any other class cluster). If you want to play with subclassing and such -- a very good idea when new to the language, assuredly -- then subclass NSObject, potentially multiple levels , and play there.
How do you mean to subclass NSObject,
potentially multiple levels? Isn't it
possible NSObject might have
conflicting methods compared to other
class clusters or just not have them
available to override?
If your goal is to figure out how method overrides work (which I thought it was), then you'd be better off doing it entirely yourself.
I may have mis-read your question.
In any case, subclassing NSString is pretty much never done. There are very very few cases where it is useful. Overriding description in anything but custom classes specifically for debugging purposes is useful, yes. Calling description in production code should never be done.
Also, why would description return an
NSString* in this code?
What would happen if something that expects an NSString* return value were to call your version that doesn't return anything?
A crash.
You are declaring a variable named strOverride of type pointer to OverrideTester. But to that variable, you are trying to assign a pointer to an NSString. You cannot assign a superclass to a variable of a subclass. Imagine a generic class TwoWheeled and a derived class Motorbike. A Motorbike can be treated like a TwoWheeled, but not the other way round as the Motorbike has features a normal TwoWheeled might not have like a motor.
I just came across Clang/LLVM today, and decided to try it out.
My system is FreeBSD8.1-Release.
The default system compiler is GCC4.2.1, which is what I have been using to compile my Objective-C project up until now.
I'm playing around with the Static Analyzer, and would like to know how to eliminate one of the warnings that is being generated.
MyClass.h
#import <objc/Object.h>
#interface MyClass: Object {
}
-(MyClass*) init;
#end
MyClass.m
#import "MyClass.h"
#implementation MyClass
-(MyClass*) init {
self = [super init];
if (self) {
// do stuff
}
return self;
}
#end
The warning:
%clang --analyze MyClass.m
MyClass.m:7:9: warning: method '-init' not found (return type defaults to 'id')
self = [super init];
^~~~~~~~~~~~
1 diagnostic generated.
I take it that the analyzer does not know how to determine super's type (Object, in this case). Is there any way to eliminate this warning (other than by suppression)? I looked into casting super, but it looks like that is not allowed.
Thanks!
Max
Update
Thanks to Dave and bbum for pointing me in the right direction for eliminating the warning. Now I'm trying to figure out why the warning occurs in the first place.
If anyone has any ideas or leads, I love the hear them.
Thanks,
Max
Two issues:
You should be inheriting from NSObject, not Object.
Your init method should return id, not MyClass*.
The warning is saying that it does not know of any method named -init at all in scope at that point. You need to import a header file that declares -init, which is probably Foundation.h or something, depending on what system you're using.
I have the following objective-C++ header with the simple method to return this pointer.
#interface MyObj
{
MyCPPObj * cpp;
}
-(MyCPPObj *) getObj;
I have created the simple method
#implementation MyObj
-(MyCPPObj *) getObj
{
return cpp;
}
Everything seems to work until I actually try to use the object in another file
newObj = [createdMyObj getObj];
It complains: error: cannot convert 'objc_object*' to 'MyCPPObje *' in initialization.
It seems that the method is return an objective-c object, but I specifically requested a C++ pointer.
MyCPPObj is an honest C++ class:
class MyCPPObj
{
public:
int x;
}
How can I fix that?
On my 10.6.3 machine, the following combination worked without any problem: aho.h
#import <Foundation/Foundation.h>
class MyCPPObj{
};
#interface MyObj:NSObject
{
MyCPPObj * cpp;
}
-(MyCPPObj *) getObj;
#end
and aho.mm
#import <Foundation/Foundation.h>
#import "aho.h"
void foo(){
MyObj*objcObj=[[MyObj alloc] init];
MyCPPObj*cppObj=[objcObj getObj];
}
Two pitfalls you might have fallen into:
Unlike C++, a class in Objective-C which doesn't inherit from NSObject won't work. (Well, you can make it work, but you don't want that usually.) Note the line #interface MyObj:NSObject.
To use NSObject, do #import <Foundation/Foundation.h>
Don't forget to use the extension .mm for Objective-C++ files.
Most likely you have forgotten to #import the header file with the #interface into the .mm file where you use getObj.
The error states what happens, and JeremyP is right on the money. When you forget to include a header file with the prototypes of the selectors, the compiler assumes the selector returns an object of type id. Well id is a typedef to objc_object*, which is incompatible with your C++ class. To fix the error, you simply need to include your header file in the file where you called getObj.