My iPhone app has an abstract class WorkingView. It is a subclass of UIView. WorkingView will in turn have a bunch of concrete subclasses. Two of those will be SimplifyView and MultiplyView.
I am trying to write the method that creates new instances of WorkingView. This method should have the mode passed in and return an instance of the appropriate concrete subclass. Per the answer to this question, this is what I have so far:
+ (id)newWorkingViewWithFrame:(CGRect)frame mode: (modeEnum) mode {
WorkingView *ret;
switch (mode) {
case simplifyMode:
ret = [[SimplifyView alloc]initWithFrame: frame];
break;
case multiplyMode:
ret = [[MultiplyView alloc]initWithFrame: frame];
break;
// additional cases here
default:
return nil;
}
// more initialization here
return ret;
}
So far, so good. But here is the problem. In SimplifyView's init method, I need to run the object through UIView's init method. Calling [super initWithFrame: frame] gets me only to WorkingView, not all the way to UIView. I suppose I could create an initWithFrame: method in WorkingView that in turn calls UIView's initWithFrame: method -- but that feels hackish.
What is the appropriate way to handle this?
If you call [super initWithFrame:frame] from your subclass, the runtime will climb the inheritance hierarchy looking for a class that implements that method, starting with super. If super doesn't implement initWithFrame:, the runtime will keep climbing until it eventually finds and invokes the default implementation in UIView.
Related
I have two class files hudlayer.m and actionlayer.m
I have a method named jump in hudlayer.m
And i have a method named jumpone in actionlayer.m
-(void) jumpone {
_heroBody->ApplyLinearImpulse(b2Vec2(_playerVelX/[_lhelper pixelsToMeterRatio], 1.25), _heroBody->GetWorldCenter());
}
and another method called jump in hudlayer.m
-(void)jump {
ActionLayer *aob = [[ActionLayer alloc] init];
[aob jumpone];
}
The problem is when i call the Jumpone method from actionlayer.m my sprite jumps (i.e method called)
My init method of action layer
- (id)initWithHUD:(HUDLayer *)hud
{
if ((self = [super init])) {
[self setupWorld];
}
return self;
}
But when i call jumpone via jump method in from hudlayer.m it fails and my app crashed.
Any help will be appreciated .thanks
the best solution for your problem is to add a tag to hudlayer & action layer
ex: hudlayer.tag=1;
actionlayer.tag=2;
and then just use getChildByTag like this:
[[[[CCDirector sharedDirector]runningScene] getChildByTag:1]jumpone];
Everytime you call jump it creates a new instance of you ActionLayer. And following that, you setup a new world and everything get tangled up. Furthermore its a memory leak.
Make you ActionLayer to an iVar of HUDLayer and call
aob = [[ActionLayer alloc] init];
in the HUDs init method.
Dont forget to release aob in dealloc of the HUDLayer
I have a main game class which renders the game using Open GL. Now I thought I could inherit from this class and then just call [super init] in its init method to get a copy of it. The plan was to make some modifications in the copy but as it seems this doesn't work.
The following is the header file of my class:
#import "GameView.h"
#interface CloneView : GameView {
}
-(id)initWithFrame:(CGRect)frame;
#end
And this is the Clone view class:
#implementation CloneView
-(id)initWithFrame:(CGRect)frame{
return [super initWithFrame:frame];
}
#end
If I set a break point in the init method in the GameView class it stops there. Thing is: my clone view doesn't get rendered, the screen stays black.
What am I missing? Thanks for your help!
Edit
Just for the record: I tried without implementing initFrame and got the same result. (as expected as the initFrame as above isn't doing anything apart from calling super)
Edit 2
I'm adding my clone to another view so I'm creating two Eagle contexts. Could that be the reason why it doesn't work?
If you are not adding anything in the init function of CloneView than you don't even have to rewrite it. You can just have your class inherit from GameView and it automatically copies it's init function.
This is from the apple docs
You should assign self to the value returned by the initializer because the initializer could return an object different from the one returned by the original receiver.
So Try doing this
-(id)initWithFrame:(CGRect)frame{
if(self = [super initWithFrame:frame] ) {
//Do whatever you need to do here.
}
return self;
}
This should fix your issue if you need to do something in your init method. Otherwise you can skip the init method altogether.
try doing this it may work..
return(self=[super initWithFrame:frame])
which ensures the super class method is copied properly to the current method
TNQ
I finally located the problem:
I needed to write a second init method. The problem was that the following code was being executed twice:
CAEAGLLayer *eaglLayer = (CAEAGLLayer *)[super layer];
[eaglLayer setOpaque:YES];
m_context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1];
if (!m_context || ![EAGLContext setCurrentContext:m_context]) {
printf("FAIL \n");
[self release];
return nil;
}
[Textures loadTexture];
if ([self createFramebuffer]) {
[self draw];
}
This code was in the initFrame method of the game class. I made a second init method that does not execute this code. Instead, this code is executed in the parent view class of the clone. Now it works, YAY!!
Thanks for trying to help me!
Can someone help me understanding as why do we call an initialization method on super first before initializing. I came across a piece of code in a subclass of UIView and wanted to know that here myMethod is always getting called that means I am not getting the frame set in UIView, then why are we doing this and using an if condition.
self = [super initWithFrame:CGRectMake(0, 0, 20, 100)];
if(self != nil) {
[self myMethod:data];
self.backgroundColor = [UIColor clearColor];
}
return self;
Let's say I have a UIView subclass called SpinningView. The code to create spinningView would look like this:
SpinningView *spinner = [[SpinningView alloc] initWithFrame:CGRectMake(0.0, 0.0, 20.0, 20.0)]
Now let's take a look at the implementation of SpinningView's -initWithFrame: method
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
self.backgroundColor = [UIColor clearColor];
}
return self;
}
The first line is simple assignment. We're assigning ourselves to the result of UIView's implementation of -initWithFrame:
We use the if statement to see if self is even valid. If it's not we just return it. If it is, we configure it.
This is simply calling the constructor of the super class (in this case UIView).
You need to call UIView's constructor to make sure that all the variables you don't see from your subclass is set up properly, and that's what you do with [super init] (or in this case initWithFrame:).
There are a few other reasons why it looks like this.
First of all [super init] might return nil. Perhaps something went wrong initializing things in the code of the superclass. The check for self != nil makes sure you don't use the object when something is already wrong with the object. It might even have been released by the super constructor.
In other cases the super constructor might actually return a different object altogether. Examples of this happening is with class clusters or when you implement a singleton.
To summarize:
Always call the designated constructor (i.e. init-method).
Always use the standard construct of if ((self = [super init])) { /* own init */ } return self;
Sometimes it looks different, but only for special reasons. If in doubt, always use (2).
Apple's documentation has a lot more info on this is you're interested.
Also, when overriding constructors like this, remember that there might be more than one constructor. For instance, if you add the view using Interface Builder, that view will be initialized using "initWithCoder:" instead. All constructors begin with "init" though.
I have a subclass of UIView called SlideOut. I want to capture the IB placement of the view when it loads, so I have this in my implementation:
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
IBframe.origin.x = frame.origin.x;
IBframe.origin.y = frame.origin.y;
IBframe.size.height = frame.size.height;
IBframe.size.width = frame.size.width;
// Initialization code.
NSLog(#"init if self x: %f, y: %f", IBframe.origin.x, IBframe.origin.y);
}
NSLog(#"init x: %f, y: %f", IBframe.origin.x, IBframe.origin.y);
return self;
}
... with a matching prototype in the .h file.
Upon starting up the simulator, I get all my other diagnostics, but neither of these log messages get called. So how does the instance actually get initialized? Or am I missing something? The position function works, but then fails because I haven't captured the actual frame of the thing. In any case, that's how I know I made the IB connections correctly.
Take a look here: Subclassing UIView, "Methods to Override", from UIView Class Reference.
In particular:
initWithCoder: - Implement this method
if you load your view from an
Interface Builder nib file and your
view requires custom initialization.
Objects that are loaded from an xib are actually unarchived. Thus, you should be using the initWithCoder: method. Alternatively, you may way to look at awakeFromNib instead.
I believe the function that gets called when the view is loaded from a NIB is initWithCoder not initWithFrame
I am trying to change the class of objects created with a nib with the iPhone SDK.
The reason for this is; i dont know until runtime what the class is that i want the nib object to be (though they will have the same UIView based super class), and i dont want to create a different nib for every eventuality - as the .nib will be the same for each, apart from the class of one object.
I have been successful, with a couple of methods, but either have some knock on effects or am unsure of how safe the methods I have used are:
Method 1: Override alloc, on the super class and set a c variable to the class I require:
+ (id) alloc {
if (theClassIWant) {
id object = [theClassIWant allocWithZone:NSDefaultMallocZone()];
theClassIWant = nil;
return object;
}
return [BaseClass allocWithZone:NSDefaultMallocZone()];
}
this works well, and i assume is 'reasonably' safe, though if have a nib with the correct class as the class identity in the Nib, or I alloc a subclass myself (without setting 'theClassIWant') - an object of the base class is created. I also dont really like the idea of overriding alloc...
Method 2: use object_setClass(self,theClassIWant) in initWithCoder (before calling initWithCoder on the super class):
- (id) initWithCoder:(NSCoder *)aDecoder {
if (theClassIWant) {
// the framework doesn't like this:
//[self release];
//self = [theClassIWant alloc];
// whoa now!
object_setClass(self,theClassIWant);
theClassIWant = nil;
return [self initWithCoder:aDecoder];
}
if (self = [super initWithCoder:aDecoder]) {
...
this also works well, but not all the subclasses are necessarily going to be the same size as the super class, so this could be very unsafe! To combat this i tried releasing and re-allocing to the correct type within initWithCoder, but i got the following error from the framework:
"This coder requires that replaced objects be returned from initWithCoder:"
dont quite get what this means! i am replacing an object in initWithCoder...
Any comments on the validity of these methods, or suggestions of improvements or alternatives welcome!
While I'm curious to see if you can pull this off using your approach, you may want to consider using custom placeholder objects.