Objective-C Simple Inheritance and OO Principles - objective-c

I have a subclass SubClass that inherits from baseclass BaseClass.
BaseClass has an initializer, like so:
-(id)init {
self = [super init];
if(self) {
[self commonInit];
}
return self;
}
-(void)commonInit {
self.goodStuff = [[NSMutableArray alloc]init];
}
SubClass does its initializer, like so:
-(id)init {
self = [super init];
if(self) {
[self commonInit];
}
return self;
}
-(void)commonInit {
self.extraGoodStuff = [[NSMutableArray alloc]init];
}
Now, I've *never taken a proper Objective-C course, but I'm a programmer more from the Electrical Engineering side, so I make do. I've developed server-side applications mostly in Java though, so I may be seeing the OO world through Java principles.
When SubClass is initialized, it calls the BaseClass init and my expectation would be — because inheritance to me implies that characteristics of a BaseClass pass through to SubClass — that the commonInit method in BaseClass would be called during BaseClass init.
It is not. I can *sorta understand maybe-possibly-stretch-my-imagination why it wouldn't be. But, then — why wouldn't it be based on the principles of OOP? What does "self" represent if not the instance of the class of the running code?
Okay, so — I'm not going to argue that what a well-developed edition of Objective-C is doing is wrong. So, then — what is the pattern I should be using in this case? I want SubClass to have two main bits — the goodStuff that BaseClass has as well as the extraGoodStuff that it deserves as well.
Clearly, I've been using the wrong pattern in this type of situation. Am I meant to expose commonInit (which makes me wonder about encapsulation principles — why expose something that, in the Java world at least, would be considered "protected" and something that should only ever be called once for each instance)?
I've run into a similar problem in the recent past and tried to muddle through it, but now — I'm really wondering if I've got my principles and concepts all straight in my head.
Little help, please.
Let me clarify — I get that self ends up being SubClass when I call init on super. That I can see when I debug, etc.
What's the pattern for overriding methods in this sort of situation? Where I have a bit of common initialization that may get called from several init methods in the super class? Do I have to put the code in every variation of init?

self and super are just a pointers to a memory location, they point to the same address where your object are allocated, but they are treated special by the Objective-C compiler:
super starts overload resolution at the first super type, ie. the parent type (BaseClass in this case).
self starts overload resolution at the current runtime type that the pointer points to. That is why you cannot call BaseClass commonInit from BaseClass, since self points to a SubClass. If you want to do this, you should have the commonInit in SubClass call [super commonInit].

self in BaseClass' constructor in your example is of type SubClass, so [self commonInit] calls SubClass' commonInit override , not BaseClass' commonInit method.

Related

super bound statically at compile time?

I want to create a class cluster with a base class and 2 subclasses. Creating an instance of the base class should return a subclass based on some conditions, but creating a subclass directly should create it. I wrote the following code in the base class:
+ (id)allocWithZone:(NSZone *)zone {
// prevent infinite recursion
if ([self isEqual:Base.class]) {
// if self is the base class, return a correct subclass
if (somecondition) {
return [SubclassA alloc];
}
return [SubclassB alloc];
}
// otherwise, alloc is called on a subclass
// call NSObject's alloc
return [super allocWithZone:zone];
}
and it works, but I'm really surprised that it does. Namely, when invoked on a subclass, why does super evaluate to the Base class's superclass (NSObject), and not the Base class (because invoked on SubclassA, the superclass is Base)? It is as if the allocWithZone: method call, inherited from Base, just always evaluated super relative to Base, not the real runtime class of the caller. I think similar code in Java and other OO languages would not work and result in infinite recursion, would it? Is this code wrong?
Your code is correct. [super ...] always uses the superclass of the class implementing the method. In your code, +allocWithZone: is implemented by class Base, so [super allocWithZone:zone] uses Base's superclass when searching for the next +allocWithZone: implementation to call.

Accessing NSObject's (superclass) version of allocWithZone while bypassing overriding (subclass) version of allocWithZone

In iOS Programming Book from Big Nerd Ranch (3rd ed) they say on pg.194
..a knowledgeable programmer could still create an instance of BNRItemStore via allocWithZone:, which would bypass our sneaky alloc trap.To prevent this possibility, override allocWithZone: in BNRItemStore.m to return the single BNRItemStore instance.
+(id) allocWithZone:(NSZone *)zone
{
return [self sharedStore];
}
This statement seems confusing to me. Doesn't this following code not prove this wrong in a way-
#import <Foundation/Foundation.h>
#interface BNRItemStore : NSObject
+(BNRItemStore *)sharedStore;
+(id)retrieveObject;
#end
#implementation BNRItemStore
+(BNRItemStore *)sharedStore{
static BNRItemStore *sharedStore=nil;
if (!sharedStore){
NSLog(#"Test2");
sharedStore= [[super allocWithZone:nil] init];
}
NSLog(#"sharedStore-> %#",sharedStore);
return sharedStore;
}
+(id)allocWithZone:(NSZone *)zone{
NSLog(#"Test1");
return [self sharedStore];
}
+(id)alloc{
NSLog(#"Retrieving super object");
NSLog(#"%#", [super allocWithZone:nil]);//Bypassing the subclass version of allocWithZone.
return [super allocWithZone:nil];
}
#end
int main(){
[[BNRItemStore alloc] init]; //the alloc message triggers a call to the subclass (overriding) version of +(id)alloc method
}
The output is:
2013-10-18 18:24:40.132 BNRItemStore[381:707] Retrieving super object
2013-10-18 18:24:40.134 BNRItemStore[381:707] BNRItemStore:0x7f8c72c091e0
If the call [super allocWithZone:nil] inside of subclass 'alloc' method would have triggered a call to subclass allocWithZone,the console would be logging "Test1" and "Test2" and finally would lead to static pointer getting allocated. But this did not happen.
This means that if we directly call [NSObject allocWithZone:nil] or [super allocWithZone:nil], the message would not redirect to the overriding version (subclass version) of allocWithZone but will give direct access to NSAllocateObject() function which does the actual allocation.
The code of +(id)allocWithZone in NSObject must look somewhat like this-
+(id)allocWithZone:(NSZone *)zone{
return NSAllocateObject();
}
Had this implementation(NSObject's allocWithZone:) included something like [self allocWithZone], the message dispatch mechanism would have included the subclass version of allocWithZone which would then make us go through the "sneaky" trap involving a call to sharedStore method.Following is the case that I'm talking about. Now if this were the case the code would definitely have infinite-looped.Clearly this isn't the case.
+(id)allocWithZone:(NSZone *)zone{
if([self allocWithZone:zone]) //this would trigger a call to subclass ver. which would call sharedStore method which would then have [super allocWithZone:nil].Infinite Loop
return NSAllocateObject();
}
So can someone clear up this query about this so called "sneaky" trap. Was the trap meant for blocking anyone from instantiating separately .i.e not being able to use NSObject's allocWithZone except when inside of sharedStore method ? Pls clarify..
The first, most important lesson here is that you should not override +allocWithZone:. I know the BNR book describes it (and the BNR book is generally very good). You shouldn't do it. I know that Apple includes some example code that does it. You shouldn't do it. (And Apple notes in the explanation that it is rare to need this.) Singletons should be created with the dispatch_once pattern.
You don't give the initial code, but I suspect that their example code overrides alloc, but not allocWithZone:. They're simply saying that if the caller uses allocWithZone:, it won't go through alloc, so they've also overridden alloc to catch that. (Of course the right answer would be just to override allocWithZone: and not alloc. But you shouldn't be overriding these methods in any case.)
EDIT:
I believe you are misunderstanding what "our sneaky alloc trap" means here. The author is assuming the following code at this point in the text:
#interface BNRItemStore : NSObject
+(BNRItemStore *)sharedStore;
#end
#implementation BNRItemStore
+(BNRItemStore *)sharedStore{
static BNRItemStore *sharedStore=nil;
if (!sharedStore){
sharedStore = [[super allocWithZone:nil] init];
}
return sharedStore;
}
#end
That's it; no +alloc overrides at all. It then points out "to enforce the singleton status…you must ensure that another instance of BNRItemStore cannot be allocated." (*)
The author goes on to suggest that we might enforce the singleton status by overriding +alloc, but immediately notes that this is insufficient, since the caller can use +allocWithZone: instead. Since it is documented that [NSObject alloc] calls [self allocWithZone:], it is necessary and sufficient to override +allocWithZone: and unnecessary and insufficient to override +alloc.
What you've done in your code is demonstrate that you can modify BNRItemStore to call [super allocWithZone:] in +alloc. That is not the point. If you can modify BNRItemStore, you could also make it a non-singleton. The point is whether an outside caller (main() in your case) can bypass the singleton instantiation, which she cannot. (**)
(*) The point it doesn't make at this point, and probably should, is that it is generally a bad idea to "enforce the singleton status" by quietly returning a singleton when the callers asked you to allocate a new object. If you need to enforce the singleton status, it is better IMO to do so with an assertion in init, since the request for a second allocation represents a programming error. That said, there are times when "transparent" singletons of immutable objects can be useful for performance reasons, such as the special singletons NSNumber provides for certain common integers, and this technique is appropriate in those cases. (By "transparent," I mean that the singleton-ness is an implementation detail that the caller should never worry about. This presumes at a minimum that the object is immutable.)
(**) Actually she can if she is determined to do so. She could always call NSAllocateObject() herself, bypassing +alloc entirely, and then call -init. This would of course be insane, and there is no reason to "protect" her from herself in doing this. It is not the job of an SDK to protect itself from the caller. It is only the job of an SDK to protect a caller from likely mistakes. The caller is never the enemy.
i'm not sure if this quite answers your question or not, but "allocWithZone:" was used back in the day to be able to partition the memory allocated. apple has since moved away from this concept and expects everything to be allocated in the same heap space. "allocWithZone:" does not even function the way it used to, and apple specifically says not to use it.

Prevent ObjC "abstract" class' init method from being called while allowing [super init]?

Say I have a pseudo-abstract base class that users should not instantiate. Basically I want to throw a warning when they're trying to call init on the class, or return one of the concrete instances with default values.
However, the concrete implementations of that base class have to call [super init] in their initializers. That should of course be allowed.
How would I best go about this?
I was thinking that this should be fine:
#implementation KTPhysicsShape
-(id) init
{
// throw exception here or return concrete instance with default values
}
// this is what subclasses would call in place of [super init]:
-(id) internal_initFromSubclass
{
return [super init];
}
#end
Any concerns about this approach? I know others could still call the internal method, but I'm mostly concerned about disallowing init since that's what users would try to call foremost.
I have also worked at the problem of how to have effectively abstract classes, but I'm not that into this solution. It seems to me that it's going to make your subclass code look weird and harder to read for casual observers.
If you require that your subclasses do particular initialization in -init, yours may the only solution. But if you just want to ensure that they have subclassed, you can do that within -init:
-(id) init
{
NSAssert(![self isMemberOfClass:[KTPhysicsShape class]],
#"KTPhysicsShape must be subclassed!");
return [super init];
}
This indicates that your architecture has a serious flaw. The whole point of the designated initializer chain is that it can be executed in a predictable order without variation. Adding contractual obligations to the subclasses to not follow the normal chain adds fragility and unneeded complexity.
The crux of the flaw is that you have an abstract class that doesn't appear to be truly abstract; it can have concrete instances and that requires concrete initialization.
First, why can't you break the class into a truly abstract class and a concrete class?
If you can't (or don't want to -- certainly, more classes has costs of its own), then one solution is to break out the commonly used initialization operations into a separate method:
- (void) commonKTPhysicsShapeInit
{
....
}
That does not call super. This would not be declared in your header; it is an internal-to-implementation-only method, thus the name.
Then, let your subclasses call through the standard designated initializer that calls commonInit. For concrete instances of that class, have a separate initializer that both calls commonInit and does the concrete initialization dance.
It is similar to what you proposed, but presents the interface in a fashion that follows existing patterns more closely.

Creating a class with no init method (Objective-c)

Is it possible to create a class with no init method so as to force all callers to create the object with a factory method instead?
So basically, you want to make sure that your class is never initialized using -init, right? You can't do exactly what you want to do, but you can come close.
Since you inherit from NSObject, you have an init method and there's nothing you can do to prevent it from being called. That said, you could override init to this:
- (id)init
{
[self dealloc];
#throw [NSException exceptionWithName:#"MyExceptionName" reason:#"Reason" userInfo:nil];
return nil;
}
This way, anytime someone calls your -init method, it kills the object, so practically speaking, your init method is pretty much un-callable.
If you really wanted to cause trouble for users of your class who use init, you can do:
#implementation MyClass
- (id) init
{
// Still have to make sure the runtime has initialised everything for "self"
self = [super init];
if (!self) return nil;
[self release]; // some say you should use [super dealloc]
[super doesNotRecognizeSelector:_cmd];
return nil;
}
#end
You invoke super's doesNotRecognizeSelector: because you might want to implement your own behaviour for unrecognised selectors for your class.
Depends. If you have your class inherit from NSObject, it will have the inherited init method (which does nothing to your instance variables). So in that sense, even if you really really wanted to not have an init method, you'd most likely still have one. So if your question was "Do I need to implement a trivial init method?", the answer is "no, you don't need to". However, if your question was "Do I need to call the init method if I didn't override it?", then the answer is "yes, you do". Whatever you do with NSObject subclasses, at some point you still need to call init after the object is created. Such is the way of life.
That being said, you most likely want an init method, unless your object initialization requires nothing more than zeroing your whole object.
Otherwise, if you choose to not inherit from NSObject or any of its subclasses and just inherit from nothing, which is clearly a bad idea because of how the NSObject class deals with everything the ObjC runtime needs to do and the requirements are quite high, then you'll potentially end up with no init method at all. But seriously, don't try this at home.
Sure. In Objective-C, there are no actual constructors. init-type methods are typically used to initialize a class, in the same vein as a constructor, but they're just a "normal" method (there's nothing special about them like there are with, e.g., Java constructors).
That said, unless your class does no initialization for its instances, you probably want to have some sort of init method.
NSObject implements an init method for you that does whatever it does. If your class has nothing to setup when it's instantiated then simply do not override the -(id)init method provided by NSObject. But you still call it when you create the instance.

Initializing a class using superclass initializer

I have got two classes, one a subclass of the other (say Animal and Dog). The superclass has got some initializers (say initAnimal), the subclass has some initializers (say initDog). The problem is that it is perfecly legal (from the compiler’s viewpoint) to do something like Dog *adog = [[Dog alloc] initAnimal], ie. initialize a class using its superclass initializer. I do not like this, because the subclass can have some extra instance variables that I want to make sure are initialized. A look into the header file solves this, but is there a simple way to make the compiler check for me? I have got a feeling I am missing something terribly obvious, but I just can’t put my finger on it :-)
Update: The initDog and initAnimal were not the best examples. I meant two really different initializers (like init for Animal and initWithFur for Dog). If I wanted every dog to have some fur assigned, I would have made the fur part of the initializer, so that nobody could obtain a dog object without a fur. But then it’s still easy to mistakenly initialize the instance with the superclass init, and then I’m hosed.
Thanks for bringing up the designated initializers, Jason. It did not occur to me before, but I could overload the designated initializer of the superclass and set some sane defaults there. But I would still prefer if I could somehow make it illegal to use other initializers than those of the class itself – any more ideas?
Generally in Objective-C you create a designated initializer for each class and then subclasses use the same initializer. So instead of using initAnimal and initDog, you just use init. The dog subclass would then define its own init method and call the designated initializer in its parent class:
#implementation Dog
-(id)init
{
if( (self = [super init]) ) { // call init in Animal and assign to self
// do something specific to a dog
}
return self;
}
#end
You don't really have to specify initDog and initAnimal because the class is declared on the right hand side of the assignment...
Update: I'm adding the following to the answer to reflect the additional information in the question
There are a number of ways to ensure that subclasses don't call initializers other than their designated initializer and the way you ultimately choose will be largely based on your whole design. One of the nice things about Objective-C is that it's so flexible. I will give you two examples here to get you started.
First, if you create a subclass that has a different designated initializer than its parent class, you can overload the parent's initializer and throw an exception. This will let programmers know immediately that they've violated the protocol for your class... however, it should be stated that you should have a very good reason for doing this and that it should be very well documented that the subclass may not use the same initializer as the superclass.
#implementation Dog
-(id)init
{
// Dog does not respond to this initializer
NSAssert( false, #"Dog classes must use one of the designated initializers; see the documentation for more information." );
[self autorelease];
return nil;
}
-(id)initWithFur:(FurOptionsType)furOptions
{
if( (self = [super init]) ) {
// do stuff and set up the fur based on the options
}
return self;
}
#end
Another way to do it is to have initializer more like your original example. In that case, you could change the default init on the parent class to always fail. You could then create a private initializer for your parent class and then make sure everyone calls the appropriate initializer in subclasses. This case is obviously more complicated:
#interface Animal : NSObject
-(id)initAnimal;
#end
#interface Animal ()
-(id)_prvInitAnimal;
#end
#interface Dog : Animal
-(id)initDog;
#end
#implementation Animal
-(id)init
{
NSAssert( false, #"Objects must call designated initializers; see documentation for details." );
[self autorelease];
return nil;
}
-(id)initAnimal
{
NSAssert( [self isMemberOfClass:[Animal class]], #"Only Animal may call initAnimal" );
// core animal initialization done in private initializer
return [self _prvInitAnimal];
}
-(id)_prvInitAnimal
{
if( (self = [super init]) ) {
// do standard animal initialization
}
return self;
}
#end
#implementation Dog
-(id)initDog
{
if( (self = [super _prvInitAnimal]) ) {
// do some dog related stuff
}
return self;
}
#end
Here you see the interface and implementation of the Animal and Dog class. The Animal is the designated top-level object and therefore overrides NSObject's implementation of init. Anyone who calls init on an Animal or any of Animal's subclasses will get an assertion error referring them to the documentation. Animal also defines a private initializer on a private category. The private category would stay with your code and subclasses of Animal would call this private initializer when they call up to super. It's purpose is to call init on Animal's superclass (NSObject in this case) and to do any generic initialization that might be necessary.
Finally, the first line in Animal's initAnimal method is an assertion that the receiver is actually an Animal and not some subclass. If the receiver is not an Animal the program will fail with an assertion error and the programmer will be referred to the documentation.
These are just two example of how you might design something with your specific requirements. However, I would strongly suggest you consider your design constraints and see if you really require this type of design as it's non-standard in Cocoa and in most OO design frameworks. For instance, you may consider making various animals root-level objects and just have an Animal protocol instead, requiring that all of the various "animals" respond to certain animal-generic messages. That way each animal (and true subclasses of Animal) can handle their designated initializers themselves and won't have to rely on superclasses behaving in such a specific, non-standard manner.