I have come across some strange behavior in my iPhone Objective-C app.
I am using some code to test an object:
if (!class_conformsToProtocol([someVar someFunctionThatReturnsAClass], #protocol(MyProtocol)))
[NSException raise:#"Invalid Argument" format:#"The variables returned by 'someFunctionThatReturnsAClass' Must conform to the 'myProtocol' protocol in this case."];
Oddly, when I have a class that looks like this:
#interface BaseClass : NSObject<MyProtocol>
...
#end
#interface SubClass : BaseClass
...
#end
And when I call this fragment: class_conformsToProtocol([SubClass class], #protocol(MyProtocol)), it returns NO.
Also, this code fails:
class_conformsToProtocol([NSString class], #protocol(NSObject)); // also returns NO
While this code returns YES:
[NSString conformsToProtocol:#protocol(NSObject)];
Is there anything I am missing in the docs?
Or is this a bug of some sort? (I am on iOS 4.2 if that matters any).
If there's a bug here, it's in the documentation.
According to the source, class_conformsToProtocol() uses class_copyProtocolList() and then tests each resulting protocol against the parameter. class_copyProtocolList() is documented as only returning protocols that the given class adopts, but not protocols adopted by superclasses. class_conformsToProtocol() therefore only tests if the given class adopts a protocol and not if its superclasses do.
The documentation bug is that class_conformsToProtocol() doesn't state this behavior. However, the documentation does state that you should generally not use that function, but instead use NSObject's conformsToProtocol: method instead.
Use NSObject's conformsToProtocol: method.
Here's an experiment I tried:
#protocol MyProtocol
- (void) doSomething;
#end
#interface MyClass : NSObject<MyProtocol>
{
}
#end
#implementation MyClass
- (void) doSomething {
}
#end
#interface MyOtherClass : MyClass
{
}
#end
#implementation MyOtherClass
- (void) doSomething {
}
#end
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
MyClass *obj_one = [MyClass new];
BOOL one_conforms = [obj_one conformsToProtocol:#protocol(MyProtocol)];
MyOtherClass *obj_two = [MyOtherClass new];
BOOL two_conforms = [obj_two conformsToProtocol:#protocol(MyProtocol)];
NSLog(#"obj_one conformsToProtocol: %d", one_conforms);
NSLog(#"obj_two conformsToProtocol: %d", two_conforms);
[pool drain];
return 0;
}
Output:
obj_one conformsToProtocol: 1
obj_two conformsToProtocol: 1
Whereas:
MyOtherClass *obj_two = [MyOtherClass new];
BOOL conforms_two = class_conformsToProtocol([obj_two class], #protocol(MyProtocol));
NSLog(#"obj_two conformsToProtocol: %d", conforms_two);
Output:
obj_two conformsToProtocol: 0
Verdict:
This is a bug with class_conformsToProtocol, use the conformsToProtocol: method of NSObject
Unlike class_conformsToProtocol, NSObject's conformsToProtocol: method will check superclasses as well.
Related
Got this code:
#import <Foundation/Foundation.h>
#interface CalculatorBrain : NSObject
- (void)pushOperand:(double)operand;
- (double)performOperation:(NSString *)op;
#property (nonatomic, readonly) id program;
+ (NSString *)descriptionOfProgram:(id)program;
+ (double)runProgram:(id)program;
#end
And this one:
#import "CalculatorBrain.h"
#interface CalculatorBrain()
#property (nonatomic, strong) NSMutableArray *programStack;
#end
#implementation CalculatorBrain
#synthesize programStack = _programStack;
- (NSMutableArray *)programStack
{
if (_programStack == nil) _programStack = [[NSMutableArray alloc] init];
return _programStack;
}
- (id)program
{
return [self.programStack copy];
}
+ (NSString *)descriptionOfProgram:(id)program
{
return #"blablabla";
}
- (void)pushOperand:(double)operand
{
[self.programStack addObject:[NSNumber numberWithDouble:operand]];
}
- (double)performOperation:(NSString *)operation
{
[self.programStack addObject:operation];
return [[self class] runProgram:self.program];
}
+ (double)popOperandOffProgramStack:(NSMutableArray *)stack
{
double result = 0;
return result;
}
+ (double)runProgram:(id)program
{
NSMutableArray *stack;
if ([program isKindOfClass:[NSArray class]]) {
stack = [program mutableCopy];
}
return [self popOperandOffProgramStack:stack];
}
#end
The code is fine an it runs, so the question is, Where is declared popOperandOffProgramStack in the interface? why it compiles and it's okay? it should crash but I can not find an explanation to this....
Thank you!
You only need to declare methods in the #interface in the .h file if you're exposing them to the world. Otherwise, no declaration needed.
And nowadays, the order that they appear in the implementation doesn't matter, either. Historically, if the method was implemented later in the #implementation than where it was invoked, you needed to have the method declared above (generally in the #interface). Now the compiler doesn't care whether the implementation is earlier or later in the .m file.
the compiler can sees its definition:
+ (double)popOperandOffProgramStack:(NSMutableArray *)stack
{
double result = 0;
return result;
}
so it is able to confirm it has been declared, the parameter types, and return type.
also - in older compilers, it would need to precede usage, but not anymore if used in the #implementation scope.
even if it were not declared, objc is weak enough that it would not be a compiler error (warning, perhaps). exception: the method must be visible if you're using ARC. the compiler needs to know the reference counting semantics and parameter types when ARC is enabled.
I have just been trying something out with a quick test and I have a question, in the following code:
#protocol stuffieProtocol <NSObject>
#required
-(void)favouiteBiscuit;
#end
.
// DOG & TED ARE IDENTICAL, THEY JUST LIKE DIFFERENT BISCUITS
#interface Dog : NSObject <stuffieProtocol>
#property (strong, nonatomic) NSString *name;
#end
#implementation Dog
- (id)init {
return [self initWithName:#"Unknown"];
}
- (id)initWithName:(NSString *)name {
self = [super init];
if(self) {
_name = name;
}
return self;
}
- (void)whoAreYou {
NSLog(#"MY NAME IS: %# I AM A: %#", [self name], [self class]);
}
- (void)favouiteBiscuit {
NSLog(#"FAVOURITE BISCUIT IS: Costa Jam Biscuit");
}
#end
.
Dog *stuffie_001 = [[Dog alloc] initWithName:#"Dog Armstrong"];
Ted *stuffie_002 = [[Ted alloc] initWithName:#"Teddy Sullivan"];
NSArray *stuffieArray = #[stuffie_001, stuffie_002];
for(id<stuffieProtocol> eachObject in stuffieArray) {
[eachObject whoAreYou]; // << ERROR
[eachObject favouiteBiscuit];
}
My question is I am getting an error "ARC Semantic Issue: No known instance method for selector 'whoAreYou'"
If I prefix [eachObject whoAreYou]; with [(Dog *)eachObject whoAreYou]; then this works for all the iterations of the loop, but that just feels wrong as the all the objects in the array are not of type Dog.
What should I be prefixing this with to be correct?
Add
-(void) whoAreYou;
to your protocol. Then the compiler knows that eachObject in the loop responds to that method.
well, you declare eachObject as an ID
that's mean that the compiler doesn't know what kind of object it is
it just know that it implements the protocol stuffieProtocol, so surely it can respond to method: favouiteBiscuit
but it doesn't know if it can respond to method whoAreYou
you can do many thing to avoid this
the easiest is:
you could ask if eachObject can perform the selector whoAreYou, and in this case you perform that selector
if ([eachObject respondsToSelector:#selector(whoAreYou) ]) {
[eachObject performSelector:#selector(whoAreYou) ];
}
this way the compiler avoid to control if eachObject implement the method whoAreYou
it will be done at runtime, so if there a method whoAreYou in eachObject, then ok, it will be called
Another way could be to make a common superclass for both ted and dog
(e.g.
SuperClassOfTedAndDog <stuffieProtocol>
)
and declare method whoAreYou in that superclass, then in your for loop use that superclass instead of ID:
for(SuperClassOfTedAndDog* eachObject in stuffieArray) {
[eachObject whoAreYou];
[eachObject favouiteBiscuit];
}
If you have a class object is there a way to use it to call class methods of that class. For example, if you have class A with method + (void)foo defined how can you achieve something like the example below without the compiler warning that it can't find the method foo:
A* object = [[A alloc] init];
id objectClass = [object class];
[objectClass foo]; // complains that the method is not found
You don't have to create instances to call Class methods. Create instance if you have to call instance methods.
Method + (void)foo of class A should be called as,
[A foo];
Creating a class method in your .h:
MyClass.h
#interface MyClass : NSObject {
}
+(void) test;
#end
MyClass.m
#implementation MyClass
-(id) init
{
self = [super init];
return self;
}
+(void) test
{
NSLog(#"Test executed");
}
#end
You can then just call it like this:
MyClass* myClass = [[[MyClass alloc] init] autorelease];
[[myClass class] test];
Edit:
If the problem is the compiler warning, just create a #protocol that defines the foo method you want to call.
Instead of calling:
id objectClass = [object class];
Try this;
id<MyProtocol> myCastedObject = (id<MyProtocol>)object;
[myCastedObject class] foo];
I just ran a quick test using one of my classes and was able to make it work using your syntax.
What errors are you getting?
What you are basically doing by the way is
[A foo];
Calling a static member of the class. This will only work with methods marked as static with the + sign.
Is there a way to somehow emulate category behavior for a class regarding to it's instance variables, not methods ?
I have a ClassA, and I want to keep its name after extending it with new methods AND ivars from other cllass (ClassB).
Of course, I can inherit ClassA, but resulting class will have different name.
For methods addition, it's not a problem - category would be a good solution.
UPDATE: ClassA used as file owner for a XIB, and these fields to be extended are IBOutlets. So I need them at build phase.
Since the iPhone uses the modern Objective-C runtime, you can use associative references to add data to instances without having to declare instance variables. See the documentation for objc_setAssociatedObject etc.
If you wrap the calls to the runtime in standard accessor methods, it will be very easy to use.
I've investigated this question playing around associative references (thanks to Ole), with methods static variables, methods swizzling, and finally come to this simple solution (no runtime stuff). I simply use "categorized" class only to return a pointer to a derived class, which of course can contain additional ivars. Doing so I achieve one unexpected benefit: I can call super's class methods, which is impossible when extending through categories.
Example of a class extension (tested):
ClassA+ClassB.h
#protocol _ClassB_Protocol
#optional // to avoid warnings
- (IBAction) onClick:(id)sender;
#property (nonatomic, retain) IBOutlet UIButton *aButton;
#end
#interface ClassA (_ClassA_Category) <_ClassB_Protocol>
#end
#interface ClassB: ClassA <_ClassB_Protocol> {
UIButton *aButton; // _ivar_ to add
}
#end
ClassA+ClassB.m
#implementation ClassA (_ClassA_Category)
// this will be called first on [ClassA alloc] or [ClassA allocWithZone:(NSZone *)zone]
+(id) alloc {
if ([self isEqual: [ClassA class]]) {
return [ClassB alloc];
} else {
return [super alloc];
}
}
#end
#implementation ClassB: ClassA
#synthesize aButton;
-(void) dealloc {
[aButton release];
[super dealloc]; // this is impossible for an ordinary category
}
- (void) onClick:(id)sender {
// some code here
}
#end
Now we have in the same time:
ClassB "extends" ClassA (category way);
ClassB inherits ClassA (ClassB can call ClassA methods);
ClassB can be accessed through ClassA name (category way)
I put Martin's example into a trivial app replacing ClassA with NSData, ClassB with XXData, and onClick with getIvar, and invoked it (Mac OS X 10.6.6, Xcode 4 Final) with:
NSData * data = [NSData data];
NSLog(#"%#", [data getIvar]);
It fails with "-[NSConcreteData getIvar]: unrecognized selector sent to instance" ..
It fails because "alloc" in the NSData category (which returns the pointer to the derived class) is not called by the above code. If, instead, "alloc" is called explicitly, as in:
NSData * data = [[NSData alloc] init];
NSLog(#"%#", [data getIvar]);
then all is well.
This is my first time using Protocols in Objective-C, and I'm running into a trouble: Here's what I've got:
I have a ReportsReceiver.h:
#protocol ReportsReceiver
-(void)receiveData:(NSArray *)theData;
#end
I have a MyController.h:
#interface MyController : UIViewController<ReportsReceiver,UITableViewDelegate,UITableViewDataSource> {
}
#end
I have a MyController.m with the implemented method:
- (void)receiveData:(NSArray *)theData {
NSLog(#"received some data!");
}
And then I have a class AllUtilities.m with the declaration:
Protocol *receiverProtocol;
AllUtilities.m also contains a method to initialize the protocol:
- (void)initProtocol {
receiverProtocol = #protocol(ReportsReceiver);
}
And then later on in AllUtilities.m I make the call:
[receiverProtocol receiveData:anArray];
Which crashes the application with the error:
2011-01-07 11:46:27.503 TestGA[91156:207] *** NSInvocation: warning: object 0x9c28c of class 'Protocol' does not implement methodSignatureForSelector: -- trouble ahead
2011-01-07 11:46:27.504 TestGA[91156:207] *** NSInvocation: warning: object 0x9c28c of class 'Protocol' does not implement doesNotRecognizeSelector: -- abort
How can I fix this? Thanks!!
You should read the part about protocols in the Objective-C guide once more :) I think you don’t really understand how protocols work. This is what you want:
// DataProducer.h
#protocol DataConsumer
- (void) handleData: (NSArray*) data;
#end
#interface DataProducer
#end
// DataProducer.m
#implementation DataProducer
- (void) generateDataAndPassTo: (id <DataConsumer>) consumer
{
NSArray *data = …;
[consumer handleData:data];
}
// SomeController.h
#import "DataProducer.h"
#interface SomeController <DataConsumer>
#end
// SomeController.m
#implementation SomeController
- (void) requestData
{
// The producer is of type DataProducer.
// Where you get it is irrelevant here.
[producer generateDataAndPassTo:self];
}
- (void) handleData: (NSArray*) data
{
NSLog(#"Got data.");
}
#end
A protocol is, in essence, a contract that says, for example, "an object conforming to the ReportsReceiver protocol must implement the receiveData: method".
So, MyController.h promises that receiveData: will be present, and MyController.m fulfills the promise. So far so good.
Now, your receiver variable doesn't care exactly what type of object the receiver is, so long as it conforms to the ReportsReceiver protocol. The way you declare that is:
id<ReportsReceiver> receiver;
...and in your initialization you might say:
receiver = myController;
Then invoke it like:
[receiver receiveData:anArray];
Start with adding the NSObject protocol to your own protocol. The warnings you are getting are methods from NSObject.
#protocol ReportsReceiver <NSObject>
-(void)receiveData:(NSArray *)theData;
#end
When declaring an object that implements a protocol, it should be more like:
id<ReportsReceiver> receiverProtocol;
or
ReceiverClass<ReportsReceiver> *receiverProtocol;
in the case that you create an object (ReceiverClass) that implements the ReportsReceiver protocol.
You assign a class that implements a protocol in the same way you assign any other class:
ReceiverClass<ReportsReceiver> *receiverProtocol;
- (void)initProtocol {
receiverProtocol = [[ReceiverClass alloc]init];
}
The #protocol directive begins declaring a protocol, not casting to one. Check out the docs for how to use them.