I'm very new with objective-C, and I am trying to implement a 'factory' style class extending a cocos2D class called CCSprite.
I want there to be a class Monster which extends CCSprite that will create and issue sprites with unique IDs so that I may call and manipulate the generated sprites later on; I am extending CCSprite to add an instance_id property and a static global_id variable.
I want to be able to use all the CCSprite constructors such as spriteWithImageNamed along with all the other functions CCSprite has, the problem is that my instance_id isn't being assigned/incremented properly(It's always 0), and I don't really know where to start to fix this.
Here is what I have:
Monster.h:
#import "CCSprite.h"
#interface Monster : CCSprite
#property (nonatomic, readonly) int instance_id;
#end
Monster.m:
#import "Monster.h"
static int global_id = 0;
#implementation Monster:CCSprite
#synthesize instance_id;
-(id) init{
self = [super init];
if(self){
instance_id = global_id;
global_id++;
}
return self;
}
#end
Is the init function called every time any constructor is called? (eg spriteWithImageNamed)?
How do I ensure my extended properties and variables are applied when I call the parent class's functions?
In Objective-C, a class's designated initializer is the one that other initializers are supposed to call into. For many classes, this is indeed init. According to the Cocos2D documentation, however, init is not the designated initializer for CCSprite. Instead, the designated initializer is:
- (id)initWithTexture:(CCTexture *)texture rect:(CGRect)rect rotated:(BOOL)rotated
So that is the method you need to override if you want all the other initializers to call your version.
(If you want to confirm for yourself that this works, you can put a log statement or breakpoint in your implementation of the designated initializer, then call other initializers and make sure the designated initializer is called.)
Your init method may or not be called depending on what init these factory methods are using internally. For example, initWithFile may call self = [super init] instead of self = [self init], in which case your implementation will not be called. You can ensure that your initialization is always performed by overriding all init methods of all superclasses and do your necessary setup. Also keep in mind that you can also override any of the factory methods, where you can get the object from calling super, and then assign it's id property before returning.
looking at CCSprite it seems that every factory method like +(id)spriteWith* calls an initWith* method
and every initWith* method at the end of the chain calls [self init]
so if you override the init method as you do in monster.m you are ok!
by the way, if you want to create your sprite using a factory method do not call [CCSprite spriteWith*], call [Monster spriteWith*] instead
best regards
Related
#interface hello:SKScene
#end
#implementation hello
+(void)method{
[self here];
}
#end
main.m
[hello method];
here,when i call this class method without allocating memory for object then method self,belong to whom????
my question is self belong to class that contain the method calling on then because i did not define object then ,why i still can use self on this?????
is it still belong to class on which it calling ??please give me proper concept of self on instance method and class method.
When you refer to self in class method, self refers to the class, itself, not to a particular instance of that class. By using self rather than the actual class name in these class methods, it makes it easier to subclass the class in question. Specifically, the class method will be inherited, but the self references will now refer to the subclass rather than the base class. If you refered to the base class by name, this would not be true.
Consider this factory method:
#implementation BaseClassObject
// THIS IS WRONG
+ (BaseClassObject *)object {
return [[BaseClassObject alloc] init];
}
#end
And consider this subclass:
#interface SubClassObject : BaseClassObject
#end
Then consider code that does:
SubClassObject *object = [SubClassObject object]; // WRONG
The problem is that the object factory method will return a BaseClassObject rather than a SubClassObject. But that is remedied if we alter the definition of that factory class method to use self:
#implementation BaseClassObject
// THIS IS RIGHT
+ (instancetype)object {
return [[self alloc] init];
}
#end
Now when I refer to [SubClassObject object], I'll get an instance of SubClassObject rather than BaseClassObject.
Note: In my contrived example, it's rather important to use self. You may, though, encounter code where it does not appear to be immediately relevant (for example, you might have a base class, but no subclass at this time).
Even in this case, you probably should be in the habit of using self in these class methods, regardless, to "future-proof" your code, so that if you ever subclass this base class at some unforeseen date in the future, these class methods are more likely to function properly even when subclassed.
Just to add a little to Rob's answer: the class object is created automatically by the compiler and/or Objective-C runtime. (It doesn't matter to you which it is.) For all intents and purposes, it's permanent. There's no need for it to be managed.
In a subclass, I'm overriding a method that is not exposed in the super class. I know that I have the correct signature as it is successfully overriding the superclass implementation. However, as part of the the new implementation, I need to call the superclass's implementation from the subclass's implementation.
Because it's not exposed I have to invoke the method via a call to performSelector:
SEL superClassSelector = NSSelectorFromString(#"methodToInvoke");
[super performSelector:superClassSelector];
However, in my application this results in an infinite recursive loop where the subclass's implementation is invoked every time I try to invoke the superclass's implementation.
Any thoughts?
I realize this is an atypical situation but unfortunately there's no way to get around what I'm trying to do.
The way I've dealt with this is to re-declare your super class' interface in your subclass implementation file with the method you want to call from the subclass
#interface MySuperclass()
- (void)superMethodIWantToCall;
#end
#implementation MySubclass
- (void)whateverFunction {
//now call super method here
[super superMethodIWantToCall];
}
#end
I'm not sure if this is the best way to do things but it is simple and works for me!
This doesn't work because you're only sending performSelector:, not the selector you pass to that, to the superclass. performSelector: still looks up the method in the current class's method list. Thus, you end up with the same subclass implementation.
The simplest way to do this may be to just write in your own call to objc_msgSendSuper():
// Top level (this struct isn't exposed in the runtime header for some reason)
struct objc_super
{
id __unsafe_unretained reciever;
Class __unsafe_unretained superklass;
};
// In the subclass's method
struct objc_super sup = {self, [self superclass]};
objc_msgSendSuper(&sup, _cmd, other, args, go, here);
This can cause problems in the general case, as Rob Napier has pointed out below. I suggested this based on the assumption that the method has no return value.
One way to go is to create a category of your class in a separate file with the method you are trying to expose
#interface MyClass (ProtectedMethods)
- (void)myMethod;
#end
and on the .m
#implementation MyClass (ProtectedMethods)
- (void)myMethod {
}
#end
Then, import this category from your .m files, and you're good to go. It's not the prettiest thing, but it'll do the trick
In Cocoa, it's recommended to always called the superclass's designated initializer. Is it safe to assume that init will always be called, during an object's initialization, even if the object isn't initialized with init?
For example, if let's say there's a class: NSThingie, which can be initialized like [[NSThingie alloc] initWithFoo: foo], or [[NSThingie alloc] initWithFoo: foo andBar: bar]. If I override init, can I assume that it will be called at some point during initialization?
EDIT
Maybe my original question wasn't worded so well. I know that overriding init causes init in superclasses to not be called, and I must explicitly call init. What I'm wondering is whether, in the apple frameworks, [NSObject init] is always called. If I initialize some Apple object like [[NSAppleClass alloc] initWithSomething: something], can I assume that NSObject's init will eventually be called during the initialization of NSAppleClass?
short answer: no
you need to make sure you are overriding the correct init method for the class you are using
for example UIView has an init method initWithFrame: and a very basic implementation of that would be:
- (id)initWithFrame:(CGRect)frame
{
self = [super init]; //calls init because UIResponder has no custom init methods
if (self){
self.frame = frame;
}
return self;
}
init is called on the superclass, but not on UIView, so if you were to override init and not initWithFrame: your initialisation code would never be run
If I override init, can I assume that it will be called at some point
during initialization?
If you override and init method you are responsible for calling the designated initializer. It is the responsibility of the developer to call correct initializer of the class that is subclassing. The reason you call the designated initializer is to make sure the class is constructed in its intended state. Not calling the correct initializer or not calling an initializer at all will likely result in undefined/undesirable behavior.
yes the "init" default constructor don't do a spécifique processing
because the init method defined in the NSObject class does no initialization; it simply returns self.
Does calling [super init] do the same thing in a category as a subclass? If not, what's the difference?
In order to understand this, it's probably important to understand the way an object is stored during runtime. There is a class object1, which holds all the method implementations, and separately, there is a structure with the storage for the instance's variables. All instances of a class share the one class object.
When you call a method on an instance, the compiler turns that into a call to objc_msgSend; the method implementation is looked up in the class object, and then run with the instance as an argument.
A reference to super takes effect at compile time, not run time. When you write [super someMethod], the compiler turns that into a call to objc_msgSendSuper instead of the usual objc_msgSend. This starts looking for the method implementation in the superclass's class object, rather than the instance's class object.2
A category simply adds methods to the class object; it has little or no relation to subclassing.
Given all that, if you refer to super inside of a category, it does indeed do the same thing that it would inside of a class -- the method implementation is looked up on the class object of the superclass, and then run with that instance as an argument.
Itai's post answers the question more directly, but in code:
#interface Sooper : NSObject {}
- (void) meth;
#end
#interface Sooper ()
- (void) catMeth;
#end
#interface Subb : Sooper {}
- (void) subbMeth;
#end
#interface Subb ()
- (void) catSubbMeth;
#end
#implementation Sooper
- (void) meth {
[super doIt]; // Looks up doIt in NSObject class object
}
- (void) catMeth {
[super doIt]; // Looks up doIt in NSObject class object
}
#end
#implementation Subb
- (void) subbMeth {
[super doIt]; // Looks up doIt in Sooper class object
}
- (void) catSubbMeth {
[super doIt]; // Looks up doIt in Sooper class object
}
#end
1 See Greg Parker's writeup [objc explain]: Classes and meta-classes
2One important thing to note is that the method doesn't get called on an instance of the superclass. This is where that separation of methods and data comes in. The method still gets called on the same instance in which [super someMethod] was written, i.e., an instance of the subclass, using that instance's data; it just uses the superclass's implementation of the method.
So a call to [super class] goes to the superclass object, finds the implementation of the method named class, and calls it on the instance, transforming it into the equivalent of [self theSuperclassImplementationOfTheMethodNamedClass]. Since all that method does is return the class of the instance on which it was called, you don't get the superclass's class, you get the class of self. Due to that, calling class is kind of a poor test of this phenomenon.
This whole answer completely ignores the message-passing/method call distinction. This is an important feature of ObjC, but I think that it would probably just muddy an already awkward explanation.
No, they do different things. Imagine a class structure like this: NSObject => MyObject => MySubclass, and say you have a category on MyObject called MyCategory.
Now, calling from MyCategory is akin to calling from MyObject, and therefore super points to NSObject, and calling [super init] invokes NSObject's -init method. However, calling from the subclass, super points to MyObject, so initializing using super invokes MyObject's -init method, which, unless it isn't overridden, behaves differently from NSObject's.
These two behaviors are different, so be careful when initializing using categories; categories are not subclasses, but rather additions to the current class.
Given the below example, super will call UIView init (not UINavigationBar init method)
#implementation UINavigationBar (ShadowBar)
- (void)drawRect:(CGRect)rect {
//draw the shadow ui nav bar
[super init];
}
#end
If you subclass it, [super init] will call UINavigationBar init method.
So yes, if there are additional things you will do in UINavigationBar init (extra from UIView) they do different things.
Edit: the following is built on a flawed premise, please look at josh's answer.
not deleting, still an interesting reference for something that could potentially lead you astray.
They are the same thing... without referencing any outside dicussions we may have had where you stated that I should ..."answer an academic question with an academic answer"
#implementation categoryTestViewController (ShadowBar)
- (void)viewDidAppear:(BOOL)animated {
//draw the shadow ui nav bar
NSLog(#"super's class = %#, self's class %#",[super class],[self class]);
if ([self class] == [super class]) {
NSLog(#"yeah they are the same");
}
}
#end
outputs:
2011-05-29 08:06:16.198 categoryTest[9833:207] super's class = categoryTestViewController, self's class categoryTestViewController
2011-05-29 08:06:16.201 categoryTest[9833:207] yeah they are the same
and calling the [super viewDidAppear:] will result in calling nothing... not a loop, so I don't know what it is really doing there.
I was reading the book "Cocoa Design Pattern" and 2 of its point, in chapter 3 (Two-Stage Creation) are making me confused.
Make sure that the superclass’ Designated Initializer is overridden to call the new Designated Initializer.
When subclassing, make sure every new initializer that isn’t the Designated Initializer calls the Designated Initializer.
My question is how we can call the method for which we don't have the parameters to pass? The book example is being posted below. In this method writer has passed some "static" values, but are we supposed to do this? Or is this always desirable?
My second question is, why I have to override the designated method of super class when I will never call it when I will be initializing my object, other than in my own designated initializer, where I will not be passing any parameters (e.g; in case of NSObject)
#interface MYCircle : NSObject {
NSPoint center;
float radius;
}
// Designated Initializer
- (id)initWithCenter:(NSPoint)aPoint radius:(float)aRadius;
#end
#implementation MYCircle
// Designated Initializer
- (id)initWithCenter:(NSPoint)aPoint radius:(float)aRadius {
self = [super init];
if(nil != self) {
center = aPoint;
radius = aRadius;
}
return self;
}
#end
// Overriden inherited Designated Initializer
- (id)init {
static const float MYDefaultRadius = 1.0f;
// call Designated Initializer with default arguments
return [self initWithCenter:NSZeroPoint radius:MYDefaultRadius];
}
Please also help me to correct my question because I am not sure what I am really asking is a correct question.
Thanks.
The designated initializer is the one that properly configures the object. If you don't choose one init... method as the designated initializer, then you have to make sure that every init... method does the right thing. That generally means that they all have to have the same code, or they all have to call a common setup method. It also means that anyone subclassing your class has to override all the init... methods instead of just one.
By picking (i.e. "designating") one init... method as the common method that all the others call, you give subclasses a single override point and a single method that their own init... methods can call to ensure that the superclass is properly configured.
If you don't have the data necessary to call the designated initializer, then you don't have the data required to set up the superclass. Somestimes you can choose reasonable default values, as above, but if not then it doesn't make any sense to create the object at hand.