Odd or erroneous warning with multiple #protocols - objective-c

I've got an odd situation where I've got two protocols and a couple classes that are initialized with delegates which implement the protocols. Standard stuff, really. However, XCode 4 is tossing up erroneous warnings that I can't seem to wrap my head around. Here's the collapsed source listing showing the definitions and implementations.
// CLASS A
#protocol ClassADelegate <NSObject>
-(void) DoSomething;
#end
#interface ClassA : NSObject { }
+(ClassA *) classWithDelegate:(id<ClassADelegate>)d;
-(ClassA *) initWithDelegate:(id<ClassADelegate>)d;
#end
#implementation ClassA
+(ClassA *) classWithDelegate:(id<ClassADelegate>)d {
return [[[ClassA alloc]initWithDelegate:d]autorelease];
}
-(ClassA *) initWithDelegate:(id<ClassADelegate>)d{
self = [super init];
return self;
}
#end
// CLASS B
#protocol ClassBDelegate <NSObject>
-(void) DoSomethingElse;
#end
#interface ClassB : NSObject<ClassADelegate> { }
+(ClassB *) classWithDelegate:(id<ClassBDelegate>)d;
-(ClassB *) initWithDelegate:(id<ClassBDelegate>)d;
-(void) DoSomething;
#end
#implementation ClassB
+(ClassB *) classWithDelegate:(id<ClassBDelegate>)d {
return [[[ClassB alloc]initWithDelegate:d]autorelease];
}
-(ClassB *) initWithDelegate:(id<ClassBDelegate>)d{
self = [super init];
return self;
}
-(void) DoSomething {
}
#end
Now here's the crazy part -- in the static constructor of ClassB on this line:
return [[[ClassB alloc]initWithDelegate:d]autorelease];
...the compiler is throwing a warning that doesn't make sense:
"Type 'id <ClassBDelegate>' does not conform to the 'ClassADelegate' protocol"
Am I missing something? The selector clearly sets the type to id<ClassBDelegate>, so d should be correct. Is this just the compiler getting confused?

The compiler is getting confused because you have methods that have the same name but different return/parameter types, and Objective-C is not exactly friends with method overloading.
When the compiler analyses:
[[[ClassB alloc]initWithDelegate:d]autorelease];
it starts with:
[ClassB alloc]
and the return type of +[ClassB alloc] is id, so the expression above is of type id. The next step is to analyse:
[##expression of type id## initWithDelegate:d]
and at this point there are two possible methods:
-(ClassA *) initWithDelegate:(id<ClassADelegate>)d;
-(ClassB *) initWithDelegate:(id<ClassBDelegate>)d;
Both are possible because the receiver is of type id, so it could be either a ClassA instance or a ClassB instance. But, aha!, the protocol of the first parameter differs, so there shouldn’t be any confusion. However, method lookup is based on two variables: whether it’s a class or instance method, and the corresponding selector. In the methods above, both are instance methods and both have the same selector. The compiler has decided to pick the first one.
One fix is to tell the compiler that the object returned by [ClassB alloc] is of type ClassB * instead of the generic type id:
return [[(ClassB *)[ClassB alloc]initWithDelegate:d]autorelease];
and maybe use this pattern in all similar classes.
Another fix is to give those methods different names, e.g. -initWithClassADelegate: and -initWithClassBDelegate:.

Related

Prevent class from being subclassed in Objective-c

How do I prevent a particular class from being subclassed?
I am not aware of such functionality (say final keyword for example) in the language. However Apple says it has done so for all classes in AddressBookUI.framework (in iOS)
For educational purposes, how can I achieve the same functionality, or how would they have done such thing?
From iOS7 Release Notes(Requires login) :
Here's one way: override allocWithZone: from within your "final" class (substituting MyFinalClassName for your actual class name) like this:
+ (id)allocWithZone:(struct _NSZone *)zone
{
if (self != [MyFinalClassName class]) {
NSAssert(nil, #"Subclassing MyFinalClassName not allowed.");
return nil;
}
return [super allocWithZone:zone];
}
This will prevent a subclass that is not a member of MyFinalClassName from being alloc'ed (and therefore init'ed as well), since NSObject's allocWithZone: must be called eventually, and by refusing to call super from your "final" class, you will prevent this.
There's a simpler way to prevent subclassing in Xcode 6 as a result of Swift interop. To prevent Swift classes from being subclassed in Objective-C the objc_subclassing_restricted is added to all class definitions in the {ProjectName}-Swift.h file.
You can use this in your projects:
#if defined(__has_attribute) && __has_attribute(objc_subclassing_restricted)
# define FOO_FINAL __attribute__((objc_subclassing_restricted))
#else
# define FOO_FINAL
#endif
FOO_FINAL
#interface Foo : NSObject
#end
#interface Bar : Foo
#end
The compiler will halt on the definition of Bar with Cannot subclass a class with objc_subclassing_restricted attribute
Here is possible solution:
#interface FinalClass : NSObject
#end
#implementation FinalClass
- (id)init
{
if (self.class != [FinalClass class]) {
return nil;
}
self = [super init];
if (self) {
// instance initialization
}
return self;
}
#end
#interface InvalidSubclass : FinalClass
#end
#implementation InvalidSubclass
- (id)init
{
self = [super init];
if (self) {
}
return self;
}
#end
I'm not sure this is 100% guaranteed because it's runtime-checking anyway, but it should be enough to block and warn people that they should not subclass this. Subclass might skip superclass's init, but then the instance will not be usable because it's not fully initialised by superclass.
Something like the following will ensure that every time an "impossible subclass" calls +alloc, an object will be allocated that is an instance of FinalClass, and not the subclass. This is essentially what NSObject's +alloc method does, but here we specify an explicit class to create. This is how NSObject allocates instances (in Obj-C 2), but there is no guarantee this will always be the case, so you may want to add an appropriate -dealloc which calls object_dispose. This method also means you don't get a nil object back if you try to instantiate a subclass - you do get an instance of FinalClass.
#interface FinalClass: NSObject
//...
+ (id)alloc; // Optional
#end
// ...
#import <objc/runtime.h>
#implementation FinalClass
+ (id)alloc {
if (![self isMemberOfClass:[FinalClass class]]) {
// Emit warning about invalid subclass being ignored.
}
self = class_createInstance([FinalClass class], 0);
if (self == nil) {
// Error handling
}
return self;
}
#end
#interface InvalidSubclass : FinalClass
// Anything not in FinalClass will not work as +alloc will
// create a FinalClass instance.
#end
Note: I'm not sure I'd use this myself - specifying that a class shouldn't be subclassed is more in the nature of a design-contract with the programmer rather than an enforced rule at compile- or runtime.

Calling Super Class's method from sub class objective C

I have a superclass and subclasses in the following format:
ParentClass.h
#interface ParentClass : NSObject
-(ParentClass *)field:(NSArray *)fields;
#end
ParentClass.m
#import "ParentClass.h"
#implementation ParentClass
-(id)init{
self = [super init];
if (self == nil) {
return self;
}
return self;
}
-(ParentClass *)field:(NSArray *)fields{
ParentClass *pc = [[ParentClass alloc] init];
// code
return pc;
}
#end
Subclass.h
#interface Subclass : ParentClass
-(Subclass *)field:(NSArray *)fields;
#end
Subclass.m
#import "Subclass.h"
#implementation Subclass
-(id)init{
self = [super init];
if (self == nil) {
return self;
}
return self;
}
-(Subclass *)field:(NSArray *)fields{
// code
return (Subclass *)[self field:fields];
}
#end
I guess the issue is here.
return (Subclass *)[self field:fields];
I'm not accessing the parent class method the way I should. Can anyone tell what should be the right way instead?
What if i call this way?
-(Subclass *)subClassField:(NSArray *)fields{
return (Subclass *)[self field:fields];
}
and i replaced the
-(Subclass *)field:(NSArray *)fields;
with
-(Subclass *)subClassField:(NSArray *)fields;
First please note that this code
-(ParentClass *)field:(NSArray *)fields{
ParentClass *pc = [[ParentClass alloc] init];
// code
return pc;
}
Doesn't look right from the software design perspective. From what you posted it seems that ParentClass instances can create and return other instances of its own type from the field method. This doesn't look ok, but it could be fine depending on what your intentions are.
Consider making ParentClass and FieldClass different classes if that makes sense.
Regarding the subclass, the way of doing what you want would be this:
-(ParentClass *)field:(NSArray *)fields
{
// code
return [super field:fields];
}
Note that I changed the returned type to be (ParentClass *), and the self to super. You cannot return a ParentClass object in the place of a SubClass object (the latter could have extra data that former doesn't know about). Doing the opposite is valid (you can return a Subclass object when someone expects to receive an object of ParentClass type).
Having said that is pretty unclear what you're trying to achieve, I'll tell what's wrong. First of all isn't enough to cast a pointer to a base class pointer, to call the superclass method, you should call it this way:
return (Subclass*) [super field:fields]; // Still wrong
But you're break polymorphism, and as the method signature says, you're returning a Subclass object, and the user that calls this method expects to have a Subclass object, but at the first call of a method that is just implemented by the subclass, it crashes because you're returning an instance of the superclass. Maybe is enough for you to change the method signature to return a ParentClass pointer, but this makes the method useless, why overriding it? It isn't pretty clear what you're trying to do, and what's your logic path.
Edit
Having seen the code that you posted on Github, here the situation is pretty different. In the Java code,t he method field returns this, so no new object gets created, and the method is just used for side effects. The add method doesn't break polymorphism, because just the object reference is of the parent class type, but if executed on a subclass it returns the object itself (this), which is of the subclass type.
In Objective-C for these cases the id type is used, which is used to represent a whatever object pointer, to a whatever class. You could also use the ParentClass type, but I'll stick to conventions. Here's an indicative code:
#implementation ParentClass
#synthesize endpoint
- (id) add: (NSString*) endpoint fields: (NSArray*) fields
{
<code>
return self;
}
- (id) field: (NSArray*) fields
{
return [self add: self.endpoint fields: fields];
}
#end
#implementation SubClass
- (id) field: (NSArray*) fields
{
< Additional code >
return [self add: self.endpoint fields: fields];
}
#end

Mismatched -init method names

I discovered an odd scenario that produces a compiler warning in XCode that I don't believe is a valid warning.
As an example I created two classes, ClassA & ClassB, that both have an init method called -initWithSomething:
One takes an (NSDate *) as the "something" and the other takes (NSString *)
Class A
// ClassA.h
#import <Foundation/Foundation.h>
#interface ClassA : NSObject {
}
-(id)initWithSomething:(NSDate *)something;
#end
// ClassA.m
#import "ClassA.h"
#implementation ClassA
-(id)initWithSomething:(NSDate *)something {
if (self = [super init]) {
}
return self;
}
#end
Class B
// ClassB.h
#import <Foundation/Foundation.h>
#interface ClassB : NSObject {
}
-(id)initWithSomething:(NSString *)something;
#end
// ClassB.m
#import "ClassB.h"
#implementation ClassB
-(id)initWithSomething:(NSString *)something {
if (self = [super init]) {
}
return self;
}
#end
Implementation of another class that uses both ClassA & ClassB
#import "ExampleClass.h"
#import "ClassA.h"
#import "ClassB.h"
#implementation ExampleClass
-(void)doSomething {
NSDate *date = [NSDate date];
NSString *string = [NSString stringWithFormat:#"Test"];
ClassA *classA = [[ClassA alloc] initWithSomething:date];
ClassB *classB = [[ClassB alloc] initWithSomething:string]; // Produces "Incompatible pointer types sending 'NSString *' to parameter of type 'NSDate *'
ClassB *classB2 = [[ClassB alloc] initWithSomething:[NSString stringWithFormat:#"Test"]]; // Does NOT produce a warning
ClassB *classB3 = [[ClassB alloc] initWithSomething:#"Test"]; // Produces the same warning as above.
[classA release];
[classB release];
[classB2 release];
[classB3 release];
}
Is this a compiler bug? Doesn't seem like either of those lines should produce a warning, especially since the line where "classB2" is initted produces no warnings.
This code actually works fine, the correct class' -initWithSomething: is called and passed the appropriate type of argument.
Clearly, more explicit method names will avoid the issue but I'd like to know why the compiler isn't able to handle this.
Note:
I should add that this only seems to occur with -init methods, any other instance or class functions do not seem to produce the warning.
I think that the problem is that +alloc returns a generic id.
This means that any method can be called on it, and the compiler will see that the first method imported that has the signature -initWithSomething is for class A, which expects an object of type NSDate *.
Also, I do believe that the method +stringWithFormat returns an id which can be compatible with NSDate.
EDIT:
A simple solution to this problem:
#interface ClassA
+(ClassA *) typeSafeAlloc;
// ...
#end
#implementation ClassA
+(ClassA *) typeSafeAlloc
{
// self is the class variable, which is the same as:
// return [ClassA alloc];
return [self alloc];
}
#end
And repeat the process with ClassB (with typeSafeAlloc returning a ClassB object)
alloc return an object of type id and therefore the compiler assumes that initWithSomething belongs to Class A (The first class interface it encounters that has the method name).
Somthing like
[(ClassB*)[ClassB alloc] initWithSomething:string]; should solve the problem.
Look at the return type of +stringWithFormat
Returns a string created by using a given format string as a template into which the remaining argument values are substituted.
+ (id)stringWithFormat:(NSString *)format, ...
(from http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSString_Class/Reference/NSString.html)
The compiler knows string is an NSString*, same with #"literals". An id however, could be anything (even an NSSDate*).

Objective C: warning on overriding init

I have a class 'DOInstance' which I inherit later on. Here's its declaration:
#interface DOInstance : NSObject {
}
- (DOInstance *) initWithSynckey:(NSString *)_synckey;
#end
Then I have a subclass of DOInstance:
#interface Workflow_Workitem_Header_1px: DOInstance {
}
//- (Workflow_Workitem_Header_1px *) initWithSynckey:(NSString *)_synckey;
#end
I go ahead and implement it in the implementation file:
- (Workflow_Workitem_Header_1px *) initWithSynckey:(NSString *)_synckey {
[super initWithSynckey:_synckey];
//..
//..
return self;
}
Now, If I do not declare initWithSynckey: (the commented declaration above) in my subclass declaration, I get a warning at the implementation: "warning: initialization from distinct Objective-C type". If I declare it, this warning goes away. Okay.
Moving on:
I later do an instantiation of my subclass:
Workflow_Workitem_Header_1px *instance;
instance = [[Workflow_Workitem_Header_1px alloc] initWithSynckey:#"xxxx"];
Now, this gives me the same warning (irrespective of whether or not I declare the corresponding initWithSynckey: selector in my subclass. Namely, "warning: initialization from distinct Objective-C type".
What am I doing wrong?
Methods named init... should have return type (id), not the type of the class. Check out NSString.h and NSArray.h (among other classes) for examples. That may be what is causing your problem.
In this case, the overriding method must return the same type as the superclass' declaration.
DOInstance defines this:
- (DOInstance *) initWithSynckey:(NSString *)_synckey;
so Workflow_Workitem_Header_1px must look like this:
#interface Workflow_Workitem_Header_1px: DOInstance {
}
- (DOInstance *) initWithSynckey:(NSString *)_synckey;
#end
Any time you get the warning "warning: initialization from distinct Objective-C type" you're doing something in contravention of your typing: changing a method signature, and the like.

Method signature for a Selector

I'm new to the Objective C business (Java developer most of the time) and am woking on my first killer app now. :-)
At the moment I am somehow confused about the usage of selectors as method arguments. They seem to be a little bit different than delegates in C# for example.
Given the following method signature
-(void)execute:(SEL)callback;
is there a way to enforce the signature for the selector passed to such a method?
The method is expecting a selector of a method with the following signature
-(void)foo:(NSData*)data;
But the SEL (type) is generic, so there is a good chance to pass a wrong selector to the
execute method. OK at least at runtime one would see a funny behavior... but I would like to see a compiler warning/error when this happens.
The quick answer is: no, there is no way to have the compiler enforce the method signature of a method selector that is provided via a SEL argument.
One of the strengths of Objective-C is that it is weakly-typed language, which allows for a lot more dynamic behaviour. Of course, this comes at the cost of compile-time type safety.
In order to do what (I think) you want, the best approach is to use delegates. Cocoa uses delegates to allow another class to implement "callback"-type methods. Here is how it might look:
FooController.h
#protocol FooControllerDelegate
#required:
- (void)handleData:(NSData *)data forFoo:(FooController *)foo;
#end
#interface FooController : NSObject
{
id <FooControllerDelegate> * delegate;
}
#property (assign) id <FooControllerDelegate> * delegate;
- (void)doStuff;
#end
FooController.m
#interface FooController (delegateCalls)
- (void)handleData:(NSData *)data;
#end
#implementation FooController
#synthesize delegate;
- (id)init
{
if ((self = [super init]) == nil) { return nil; }
delegate = nil;
...
return self;
}
- (void)doStuff
{
...
[self handleData:data];
}
- (void)handleData:(NSData *)data
{
if (delegate != nil)
{
[delegate handleData:data forFoo:self];
}
else
{
return;
// or throw an error
// or handle it yourself
}
}
#end
Using the #required keyword in your delegate protocol will prevent you from assigning a delegate to a FooController that does not implement the method exactly as described in the protocol. Attempting to provide a delegate that does not match the #required protocol method will result in a compiler error.
Here is how you would create a delegate class to work with the above code:
#interface MyFooHandler <FooControllerDelegate> : NSObject
{
}
- (void)handleData:(NSData *)data forFoo:(FooController *)foo;
#end
#implementation MyFooHandler
- (void)handleData:(NSData *)data forFoo:(FooController *)foo
{
// do something here
}
#end
And here is how you would use everything:
FooController * foo = [[FooController alloc] init];
MyFooHandler * fooHandler = [[MyFooHandler alloc] init];
...
[foo setDelegate:fooHandler]; // this would cause a compiler error if fooHandler
// did not implement the protocol properly
...
[foo doStuff]; // this will call the delegate method on fooHandler
...
[fooHandler release];
[foo release];
To directly answer your question, no, the SEL type allows any type of selector, not just ones with a specific signature.
You may want to consider passing an object instead of a SEL, and document that the passed object should respond to a particular message. For example:
- (void)execute:(id)object
{
// Do the execute stuff, then...
if ([object respondsToSelector:#selector(notifyOnExecute:)]) {
[object notifyOnExecute:self];
}
// You could handle the "else" case here, if desired
}
If you want to enforce the data handling, use isKindOfClass inside your selector. This works a lot like instanceof which you are familiar with in Java.