Is it an acceptable template for an init method to return an object of a different type?

I am working on fixes for some existing objective-c code and came across what I found strange:

@interface ClassA : UIView
...

static ClassA* oldSelf = nil;

@implementation
- (id)initWithFrame:(CGRect)frame {
    oldSelf = self;
    self = [[ClassB alloc] initWithFrame:(CGRect)frame]; // xcode warns: Incompatible pointer types assigning to "ClassA *" from "ClassB *"
    //       ^^^^^^ Is this ok?
    [oldSelf release];
    return self;
}


@interface ClassB : UIView
...

@implementation
- (id)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    return self;
}

      

It's all wrapped up in an old library ... The public gets the file lib.a and ClassA.h In the code using the library, this happens:

#import "ClassA.h"

...

// useage
ClassA *myA = [[ClassA alloc] initiWithFrame:CGRectMake(0,0,100,100)];
...

      

So we have an initializer for ClassA that actually returns an unrelated class. ClassA and ClassB respond to the same messages, so they compile and run. It seems like ClassA is being used to shade some of the functions exposed in ClassB?

I am curious if this is acceptable behavior and if it is a known pattern, what is it called? Are there any side effects for this setup?

=============================================== === =======

Thanks for answers! I think I got it ... in short, not a normal sample, not exactly a good idea.

  • Kind of like a cluster of classes (abstract factory), but not really, because a generic abstract class must be returned. And since the code never seems to be going to return anything other than a ClassB object, perhaps not what the original author was thinking.
  • More like a proxy, but not implemented correctly. ClassA must contain a private instance of ClassB and pass messages between them.

=============================================== === =======

Edited: added "oldSelf" parts ...

Edited: Added static library details ...

Edited: Added accepted reply message ...

+3


source to share


7 replies


The main drawback I see here is: The user ClassA

expects the object he just created via [[ClassA alloc] initWithFrame:...]

to return YES for [object isKindOfClass:[ClassA class]

.
It can also lead to errors when using things like NSInvocation

because the wrong class will be used to define the method signature, although I'm not sure about that.

Due to the dynamic nature of Objective-C, this will work as you describe, but it can be confusing and I would strongly discourage anyone from using this pattern.



As pilavdzice said, the "correct" alternative would be to have both ClassA

and ClassB

inherit from another class ( abstact superclass ), which then in its initializer decides what the particular subclass is . Good examples of this pattern, called cluster clusters, are NSString

, NSArray

and NSDictionary

, which return objects of different subclasses based on their initialization, which is also the reason why you cannot subclass them directly without any effort.

+3


source


This is not an unreasonable thing in all cases, but it is difficult to say if this is a good thing in the situation you are describing. Two examples where this might be good:

  • The initializer returns an instance of a more specialized subclass. For example, you can choose different implementations of the data structure depending on the number of items to store.

  • The initializer returns some kind of proxy object.



Your code looks a little weird. At the very least, I expect to see the cast as a signal (for both the compiler and future programmers) that the author knew what he was doing. A comment explaining why a different object type was returned would not hurt either. Ideally ClassB

should be a subclass ClassA

as it was supposed to provide the same interface.

+3


source


Clusters of classes are implemented this way, sorted. The associated isa

-swizzling method can be used to implement a kind of state machine. This requires the same ivar layout to work. As far as side effects go, I believe it can break KVO; but someone can correct me about this.

+2


source


In user code, of course, it is not easy to return an unrelated class, however, in some of the Apple frameworks, a more specific version of the class with the same public interface is common.

Apple Cocoa Fundamentals discusses in some detail the fact that objects like NSArray and NSNumber can return a different object than the class you're asking for.

+1


source


This is not a pattern that I am aware of.

If I understand this correctly, the normal way to do this would be for both classes to inherit from the same abstract base class.

0


source


As @alan duncun points out, this technique is called class cluster and is quite common. But your implementation is slightly wrong. You should never return an incompatible type. In your example, ClassB must inherit from ClassA.

0


source


Well, it's kind of like NSScanner .

Thus, the inner class is not affected and cannot be misused. ClassB cannot be initialized anywhere other than the ClassA implementation file.

This makes sense if you have multiple inner classes and your initializer is somehow deciding which class is actually needed.

I don't see any benefit if you only use one inner class.

0


source







All Articles