I seek help in understanding why code behaves so strange. What I have: BaseClass : NSManagedObject and ChildClass : BaseClass
BaseClass has a category. .h:
#interface BaseClass (Category)
+ (NSArray)method;
#end
.m:
#implementation BaseClass (Category)
+ (NSArray *)method
{
if ([self isKindOfClass:[ChildClass class]) {
do stuff
return resultArray;
}
return nil;
}
From another place in project I call *array = [ChildClass method];. In the BaseClass (Category) implementation console reads self = (Class)ChildClass, but still the execution flow skips the if and passes right to return nil; for some reason, that is beyond my understanding. Any suggestions why that may be? All answers are appreciated. Thanks.
... as I don't have enough rep points, I'm not posting the screenshots. Hope I was clear.
You're in a static method, so self represents the class itself. Is enough to do this:
if (self==[ChildClass class]) {
do stuff
return resultArray;
}
You might be better off doing [[childOrBaseInstance class] method]; in your code that is calling +method.
ChildClass would have it's own +method that override's BaseClass's +method.
Any time you check your class and do something different, ask yourself if you're just manually recreating polymorphism.
Related
it seem like when messaging a super class from a subclass that has overridden some methods, you can't get the "original" implementation by just using super. any work arounds or should i init that super class?
here some code showing what i mean:
#interface ClassA : NSObject
- (void)method1;
#end
#implementation ClassA
- (void)method1
{
[self method2];
}
- (void)method2
{
NSLog(#"Hello it's ClassA");
}
#end
#interface ClassB : ClassA
- (void)method3;
#end
#implementation ClassB
- (void)method2 // overriding method2
{
NSLog(#"Hello it's ClassB");
}
- (void)method3
{
[self method1]; // logs "Hello it's Class B" as expected
[super method1]; // still logs "Hello it's ClassB" instead of "Hello it's ClassAā€¯!?
}
#end
thanks in advance for any help :)
It is behaving as expected; you've instantiated an instance of ClassB and, this, when method1 calls [self method2], the method dispatch follows the normal lookup path. self is an instance of ClassB.
Copy/paste this into all your methods:
NSLog(#"%s %p %#", __PRETTY_FUNCTION__, self, self);
That should make it clear what is going on.
any work arounds?
Yes, don't do this as it is poor design. A superclass second guessing inheritance is a sure fire way to end up with an unmaintainable mess of a code base.
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
I read a topic at here
http://www.mikeash.com/pyblog/friday-qa-2010-05-14-what-every-apple-programmer-should-know.html. Mike said that "Always use [self class] when invoking your own class methods". But I don't understand why. Can you give an example ?
Lets say that you have class foo, which have the following methods:
+(NSString*) bar{ return #"bar"; }
-(NSString*) barMethod{ return [[self class] bar]; }
Now lets say that you have a class foo2 which inherits from foo. If you override
+(NSString*) bar { return #"bar2" }
the method barMethod will return bar2, as you probably intended for it to.
Unlike other OO languages, class methods in Objective-C are both inherited and can be overridden.
Thus, if you have:
#immplementation Abstract // : NSObject
- (void) doSomethingClassy
{
[Abstract classyThing];
}
+ (void) classyThing
{
... some classy code ...;
}
#end
#interface Concrete : Abstract
#end
#implementation Concrete
+ (void) classyThing
{
... some classy code ...;
[super classThing];
}
#end
Then this won't call Concrete's +classyThing from Abstract's implementation of doSomethingClassy:
[[[Concrete alloc] init] doSomethingClassy];
Whereas if you modify doSomethingClassy to do [[self class] classyThing]; it'll work as expected.
(note that this is a concrete example of Liye Zhang's answer -- feel free to mark his correct as he was first, just not with quite as concrete of an example)
How does one detect the calling class from within a static method such that if the class is subclassed the subclass is detected? (See comment inside MakeInstance)
#interface Widget : NSObject
+ (id) MakeInstance;
#end
#implementation Widget
+ (id) MakeInstance{
Class klass = //How do I get this?
id instance = [[klass alloc] init];
return instance;
}
#end
#interface UberWidget : Widget
//stuff
#end
#implementation UberWidget
//Stuff that does not involve re-defining MakeInstance
#end
//Somewhere in the program
UberWidget* my_widget = [UberWidget MakeInstance];
I believe the appropriate solution for what you are trying to accomplish is this:
+ (id) MakeInstance{
id instance = [[self alloc] init];
return instance;
}
And as Cyrille points out, it should probably return [instance autorelease] if you want to follow convention (and aren't using ARC).
UIAdam's solution is perfectly fine for your case. Although if you want to detect, more specifically, from which class is you method called, use [self class] on objects, or simply self for classes.
I have an Objective-C class that has a method that is meant to be overridden, which is uses in a different method. Something like this:
#interface BaseClass
- (id)overrideMe;
- (void)doAwesomeThings;
#end
#implementation BaseClass
- (id)overrideMe {
[self doesNotRecognizeSelector:_cmd];
return nil;
}
- (void)doAwesomeThings {
id stuff = [self overrideMe];
/* do stuff */
}
#end
#interface SubClass : BaseClass
#end
#implementation SubClass
- (id)overrideMe {
/* Actually do things */
return <something>;
}
#end
However, when I create a SubClass and try to use it, it still calls overrideMe on the BaseClass and crashes due to doesNotRecognizeSelector:. (I'm not doing a [super overrideMe] or anything stupid like that).
Is there a way to get BaseClass to call the overridden overrideMe?
What you are describing here should work so your problem is likely elsewhere but we don't have enough information to help diagnose it.
From your description, I'd say either the instance you're messaging is not the class you think it is or you made some typo in your code when declaring the method names.
Run your application under gdb, add a symbolic breakpoint on objc_exception_throw, reproduce your problem. Once your process has stopped on the "doesNotRecognizeSelector" exception, print object description and it's class.
Or log it before calling -overrideMe:
NSLog(#"object: %# class: %#", obj, [obj class])
Write a category for BaseClass to override the method.
#interface BaseClass (MyCategory)
- (id) overrideMe;
#end
#implementation BaseClass (MyCategory)
- (id) overrideMe
{
/* Actually do things */
return <something>;
}
#end
Now all instances of BaseClass will respond to selector overrideMe with the new implementation.