Parent.h (extends NSObject)
like this:
#implementation InTParent
-(id)init
{
NSLog(#"Parent init method");
if (self = [super init]) {
;
}
return self;
}
-(id)initWithName:(NSString*)name;
{
NSLog(#"Parent initWithName method");
if (self = [self init]) {
;
}
return self;
}
Son.h(extends Parent)
like this:
#implementation InTSon
-(id)init
{
NSLog(#"Son init method");
if (self = [super init]) {
;
}
return self;
}
-(id)initWithName:(NSString*)name;
{
NSLog(#"Son initWithName method");
if (self = [super initWithName:name]) {
;
}
return self;
}
I use this: IntSon *_son = [[IntSon alloc] initWithName:#"asd"];
why the output is:
Son initWithName method --> Parent initWithName method --> Son init method --> Parent init method
But in Java,it perhaps like this :
Son initWithName method --> Parent initWithName method --> Parent init method
Please help me!
To understand this behavior, you have to understand how Objective-C message dispatching works. And this is a good example to illustrate it.
At a high level, any time you call a method on any object, the Objective-C runtime looks for the implementation provided the most-derived (deepest in the class hierarchy) class. If it doesn't find it, it will go the next most-derived, and so on all they way up to NSObject. The first time it finds an implementation matching the selector (method name, roughly), it will execute that implementation. When you call super, you are specifying to send a message to the next most-derived class's implementation of that method.
So in your code, you call alloc on the InTSon class, which returns an instance of IntSon with the isa pointer set to the class object InTSon. The isa pointer is how method implementations are looked for during the process of ascending the class hierarchy.
So after you have an InTSon instance, you call initWithName: on it, and it checks the class pointed to by isa (which is InTSon for an implementation of this method. It finds it, so executes it, resulting in your first output:
"Son initWithName method"
Immediately afterward, you call the superclass implementation of that method, which looks in InTParent for its implementation of initWithName: and executes that code, resulting in your second output:
Parent initWithName method
Now here is where you see a deviation from Java - you call init on self. self, however, is a pointer to an InTSon instance. So when the runtime resolves this message, it looks first for the implementation of init in the InTSon class. Of course, it finds it, and executes the code for that method, which gives you your third output, Son init method. Next you call super, which looks up and executes the InTParent implementation of init, and gives you your final output.
To sum up, regardless of where in the class hierarchy a method is called from, if it's called on self, it will always execute the most derived implementation of that method. Hope this helps, and if you have any questions please let me know!
Your class diagram looks like this:
When you send the initWithName: message to your instance of InTSon, the system looks up the method in InTSon's method table, and finds the method that we call -[InTSon initWithName:]. That method, simplified, looks like this:
// -[InTSon initWithName:]
- (id)initWithName:(NSString *)name {
return [super initWithName:name];
}
This method does [super initWithName:name]. Because it sends the message to super, the system looks in the method table of the superclass of self's class. Your object's class is InTSon, and its superclass is InTParent. So when -[InTSon initWithName:] does [super initWithName:name], the system looks for the method in InTParent's method table. It finds the method that we call -[InTParent initWithName:]. That method, simplified, looks like this:
// -[InTParent initWithName:]
- (id)initWithName:(NSString *)name {
return [self init];
}
This method does [self init]. Because it sends the message to self, the system looks in the method table for self's class. Even though we are in -[InTParent initWithName:], self is still an instance of the InTSon class. So the system looks for an init method in InTSon's method table. It finds the method we call -[InTSon init]. That method, simplified, looks like this:
// -[InTSon init]
- (id)init {
return [super initWithName:#"sdas"];
}
This method does [super initWithName:], so the system looks in the superclass's (InTParent's) method table and finds the -[InTParent initWithName:] method. And as we have just seen, -[InTParent initWithName:] ends up calling [InTSon init]. So we get infinite recursion and the app crashes.
To solve this problem, you need to pick one of InTParent's init methods as its designated initializer. You should read about designated initializers in Apple's documentation.
You should pick the initWithName: method as InTParent's designated initializer. This method must not send any init message to self. It should only send super's designated initializer message (which in the case of NSObject is the init message), and it should send it to super. So you need to write the -[InTParent initWithName:] method like this:
- (id)initWithName:(NSString *)name {
// init is NSObject's designated initializer, and we send it to super.
if ((self = [super init])) {
// your initialization code here
}
return self;
}
By picking a designated initializer, and making your designated initializer only send the superclass's designated initializer message to super, you prevent the infinite recursion.
Because "self" is a "InTSon", so [self init] calls the "InTSon" init method.
Related
I have the following method:
- (void) someMethod
{
if ([super respondsToSelector:#selector(someMethod)])
{
[super performSelector:#selector(someMethod)
withObject:nil];
}
}
someMethod does not exist on superclass. as i understand, if there is no such method, runtime will ask the next responder in chain for such method till the NSObject class. And i was sure, that if statement will return NO.
Statement return YES. After that it performs selector without crash. As result - infinite recursion.
so, i have two questions:
Why [super respondsToSelector:#selector(someMethod)] returns YES ?
Why [super performSelector:#selector(someMethod) withObject:nil] does not crash with error 'does not responds to selector' ?
I think i've missed something essential.
Please, help.
Yes, you missed something essential as you suggest. From the documentation for respondsToSelector:
You cannot test whether an object inherits a method from its superclass by sending respondsToSelector: to the object using the super keyword. This method will still be testing the object as a whole, not just the superclass’s implementation. Therefore, sending respondsToSelector: to super is equivalent to sending it to self. Instead, you must invoke the NSObject class method instancesRespondToSelector: directly on the object’s superclass, as illustrated in the following code fragment.
if( [MySuperclass instancesRespondToSelector:#selector(aMethod)] )
{
// invoke the inherited method
[super aMethod];
}
HTH
I want to subclass UIBarButtonItem with an initializer that automatically sets the image, target and selector.
I tried to override the - (id) init message like this:
- (id) init {
self = [super initWithBarButtonSystemItem:UIBarButtonSystemItemBookmarks target:self action:#selector(myMessage)];
if (self) {
//Custom initialization
}
return self;
}
This results in an unwanted recursion, because initWithBarButtonSystemItem:target:action calls init.
I read the answer to the following question, but it didn't help me as it still gives me the same recursion:
Custom init method in Objective-C, how to avoid recursion?
The code I used when testing this was the following:
- (id)initWithBarButtonSystemItem:(UIBarButtonSystemItem)systemItem target:(id)target action:(SEL)action {
return [super initWithBarButtonSystemItem:systemItem target:target action:action];
}
- (id) init {
self = [self initWithBarButtonSystemItem:UIBarButtonSystemItemBookmarks target:self action:#selector(myMessage)];
if (self) {
//Custom initialization
}
return self;
}
I have the same requirements as the author of the mentioned question:
I don't want to create a custom init like - (id) initCustom.
I don't want to use a flag to determine if the init has already been run once.
I also want to mention that I tried to create the UIBarButtonItem without using the initWithBarButtonSystemItem:target:action initializer - but I couldn't find a #property corresponding to the first parameter.
Thank you!
As mentioned in the link you have provided (see the accepted answer), if you initialise with super call, it will not result in recursive calls.
Probably you should have something like this:
- (id) init {
self = [super initWithBarButtonSystemItem:UIBarButtonSystemItemBookmarks target:self action:#selector(myMessage)];
if (self) {
//Custom initialization
}
return self;
}
You may find this helpful: Concepts in Objective-C: Object Initialization
In particular, pay attention to the concept of the designated initializer:
The designated initializer plays an important role for a class. It ensures that inherited instance variables are initialized by invoking the designated initializer of the superclass. It is typically the init... method that has the most parameters and that does most of the initialization work, and it is the initializer that secondary initializers of the class invoke with messages to self.
When you define a subclass, you must be able to identify the designated initializer of the superclass and invoke it in your subclass’s designated initializer through a message to super. You must also make sure that inherited initializers are covered in some way. And you may provide as many convenience initializers as you deem necessary. When designing the initializers of your class, keep in mind that designated initializers are chained to each other through messages to super; whereas other initializers are chained to the designated initializer of their class through messages to self.
This is why you are seeing this behavior. initWithBarButtonSystemItem:target:action is the designated initializer for that class, and it in turn calls init. Overriding init to call a designated initializer won't for the very reasons you are describing.
Instead, you can create a convenience constructor that calls the designated initializer:
+ (instancetype) bookmarksBarButtonItemWithAction:(SEL)action {
return [[[self class] alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemBookmarks target:self action:action];
}
Which would achieve pretty much what you are looking for, and does not even require that you subclass UIBarButtonItem. It can be done as a category.
I have a UIViewContoller subclass BaseViewControler. It has a method named -initWithStyle:. If I subclass that class as SubBaseViewContoller,
what should -[SubBaseViewController init] look like?
My code:
- (id)init
{
self = [self initWithStyle:kGreenStyle];
if (self) {
// stuff
}
return self;
}
In SubBaseViewController I don't have initWithStyle:
and my app crashed randomly with the -init above, I checked other view controllers which are subclasses of BaseViewController, and they use self = [super initWithStyle:kGreenStyle], and work. What's the explanation?
A UIViewController has two designated initializers:
- (id)initWithNibName:(NSString *)nibName bundle:(NSBundle *)nibBundle;
and
- (instancetype)initWithCoder:(NSCoder *)aDecoder;
The second form will be invoked when Interface Builder is used to define a View Controller with a resource, that is nib files ore segues. The first form can be used when you manually create a view controller.
You can override these initializers as follows:
// Designated initializer
- (id)initWithNibName:(NSString *)nibName bundle:(NSBundle *)nibBundle
{
self = [super initWithNibName:nibName bundle:nibBundle];
if (self) {
; // your initialization
}
return self;
}
// Designated initializer
- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self) {
; // your initialization
}
return self;
}
Note: The method init will not be called when created from a resource. When you invoke init yourself, it will eventually call initWithNibName:bundle: with nil arguments.
See also: Reference: Multiple Initializers and the Designated Initializer
Another possible solution for your problem:
One approach to provide a method to initialize a custom View Controller is to use a "convenience class method". In this case, you don't need to override the designated initializers, instead you provide properties in your subclass.
The convenience class method may then look like as shown below:
// MyViewController
+ (instancetype) myViewControllerWithStyle:(Style)style
{
// Possibly get the bundle and the nib name (or leave it nil)
...
// Invoke a designated initializer with suitable parameters:
MyViewController* viewController = [[MyViewController alloc] initWithName:nibName
bundle:bundle];
viewController.style = style;
return viewController;
}
You should use super instead of self:
- (id)init
{
self = [super initWithStyle:kGreenStyle];
if (self) {
}
return self;
}
If you do it that way you force just parent class to do all the initialisation which it supposed to do. And if you want to do something in init method just in your subclass you add it here:
- (id)init
{
self = [super initWithStyle:kGreenStyle];
if (self) {
// do some stuff you need to do in subclass initialisation
// for example init variable specific just for that class (not parent class)
}
return self;
}
This is kind of a synthesis of Greg's and CouchDeveloper's answers.
But really, it's a defense of Greg's answer.
First, yes, understand designated initializers. They are important, and CouchDeveloper
is right to emphasize this.
Second, nothing prevents a subclass of UIViewController from having
completely different designated initializers (preferably one) of your own choosing.
If you are creating UIViewControllers outside of Interface Builder,
as it appears the OP may be doing, the primacy of -initWithNibName:Bundle:
and -initWithCoder: becomes silly.
Apple say this:
When you define a subclass, you must be able to identify the designated initializer of the superclass and invoke it in your subclass’s designated initializer through a message to super. You must also make sure that inherited initializers are covered in some way.
Given that, here's what the OP's BaseViewController might look like:
const NSUInteger kDefaultStyle = 0; // just to have something to use
#implementation BaseViewContoller
// No longer valid for *this* class.
// Should have one for -initWithCoder: too, but elided for this example.
- (instancetype)initWithNibName:(NSString *)nibNameOrNil
bundle:(NSBundle *)nibBundleOrNil
{
[self doesNotRecognizeSelector:_cmd];
return nil;
}
// New designated initializer for *this* class.
// This should call the superclass's designated initializer.
- (instancetype)initWithStyle:(NSUInteger)style
{
// use the designated initializer of the superclass to init
if ((self = [super initWithNibName:nil bundle:nil])) {
// stuff
}
return self;
}
// in *other* initializers, you *should* call *your* class's
// designated initializer.
- (instancetype)init
{
return [self initWithStyle:kDefaultStyle];
}
#end
Given this, I think CouchDeveloper's "this is wrong" calling out in the comments
of Greg's answer is itself wrong. This is because:
BaseViewController is unseen, so we can't possibly know what its designated initializer is, regardless of its superclass.
OP has given every indication that -[BaseViewController initWithStyle:] is the designated initializer
OP is perfectly free to declare [SubBaseViewController init] as the designated initializer, so long as he follows the rules.
Finally, unrequested advice for the OP:
Assuming those aren't typos in your original post, you should always capitalize the
first letter of class names. BaseViewController, not baseViewController.
You can swim against the idiom is when you
become a total badass; by that point, I'll wager you'll not want to.
I have seen self = [super init] in init methods. I don't understand why. Wouldn't [super init] return the superclass? And if we point self = [super init], are we not getting self = superclass?
Here's an example code fragment
- (id)init
{
if (self = [super init]) {
creationDate = [[NSDate alloc] init];
}
return self;
}
Hope someone can clarify this for me. Thank you.
Assuming that MyClass is a subclass of BaseClass, the following happens when
you call
MyClass *mc = [[MyClass alloc] init];
[MyClass alloc] allocates an instance of MyClass.
The init message is sent to this instance to complete the initialization process.
In that method, self (which is a hidden argument to all Objective-C methods) is
the allocated instance from step 1.
[super init] calls the superclass implementation of init with the same (hidden)
self argument.
(This might be the point that you understood wrongly.)
In the init method of BaseClass, self is still the same instance of MyClass.
This superclass init method can now either
Do the base initialization of self and return self, or
Discard self and allocate/initialize and return a different object.
Back in the init method of MyClass: self = [super init] is now either
The MyClass object that was allocated in step 1, or
Something different. (That's why one should check and use this return value.)
The initialization is completed (using the self returned by the superclass init).
So, if I understood your question correctly, the main point is that
[super init]
calls the superclass implementation of init with the self argument,
which is a MyClass object, not a BaseClass object.
As you have Question self = [super init] in the if Condition suggest a specific meaning.
First of all [super init] gives the initialization of the superclass of the existing class which is in use currently. Using [super init] gives the super class initialization which shows that object exist of the class.
Now when you use self = [super init] that means you are assigning the class to the self for the further utilization of the same class.
And at the end you put it in if condition as if(self = [super init]) this means you are checking whether the object of the class exist of not to prevent the foul behavior of the application.
I think it is clear now!!!
#MartinR has a very good answer. But do you ever wonder why "[super init] calls the superclass implementation of init with the same (hidden) self argument. (This might be the point that you understood wrongly.)" works in his 3rd point ?
Here is the excerpt from Big Nerd Ranch guide 3rd edition, chapter 2 Objective C that clarifies this point
“How does super work? Usually when you send a message to an object,
the search for a method of that name starts in the object’s class. If
there is no such method, the search continues in the superclass of the
object. The search will continue up the inheritance hierarchy until a
suitable method is found. (If it gets to the top of the hierarchy and
no method is found, an exception is thrown.)”
“When you send a message to super, you are sending a message to self,
but the search for the method skips the object’s class and starts at
the superclass.”
This code shows how iOS Runtime performs this task
objc_msgSendSuper(self, #selector(init));
Every method that you declare has two hidden parameters: self and _cmd.
The following method:
- (id)initWithString:(NSString *)aString;
is converted by the compiler to the following function call:
id initWithString(id self, SEL _cmd, NSString *aString);
see this link for more:
http://www.cocoawithlove.com/2009/04/what-does-it-mean-when-you-assign-super.html
Self = [super init];
According to JAVA, this mean a pointer to instance itself so object can message itself.
Same meainng of Self here in objective C,
According to JAVA, Super mean that allow to access base or parent class
Same meainng of Super here in objective C,
Now init instance to to complete the initialization process.
I would think of it as, init'ing all the supers variables etc, then you get to init your extended classes variables before it is returned.
[super init] is the same as [self superclass_variant_of_init]
If you want to send a message to superclass, there is another approach (without using runtime library):
[[self superclass] init];
From Apple's Documentation:
Because an init... method might return nil or an object other than the one explicitly allocated, it is dangerous to use the instance returned by alloc or allocWithZone: instead of the one returned by the initializer. Consider the following code:
id myObject = [MyClass alloc];
[myObject init];
[myObject doSomething];
The init method in the example above could have returned nil or could have substituted a different object. Because you can send a message to nil without raising an exception, nothing would happen in the former case except (perhaps) a debugging headache. But you should always rely on the initialized instance instead of the “raw” just-allocated one. Therefore, you should nest the allocation message inside the initialization message and test the object returned from the initializer before proceeding.
id myObject = [[MyClass alloc] init];
if ( myObject ) {
[myObject doSomething];
} else {
// error recovery...
}
I am in a situation where I have to create an init with a parameter.
- (id) initWithSomeParameter:(id)parameter;
Normal init should not be called. So, I have two choices to accomplish this. I can actually throw an exception when some one calls the init or return nil.
Which should be done and why?
You should have a designated initializer, which all other init methods call.
In your example, the designated initializer would perhaps be
- (id)initWithSomeParameter:(id)something;
If another programmer calls -(id)init instead of the designated initializer, you might do something like this:
- (id)init
{
return [self initWithSomeParameter:nil];
}
If you absolutely must have a value provided for someParameter, you can raise an exception if you choose to, but this practice varies with how important getting a value is on initialization of the instance object.
Here's Apple's input also on using a designated initializer:
http://developer.apple.com/library/ios/#documentation/general/conceptual/DevPedia-CocoaCore/MultipleInitializers.html
It's hard to say without knowing more about your use case. However, Perhaps you are being too strict with your API.
UIKit itself is full of classes that need some data to operate, but don't require it on init. Take UIImageView for example. You may think "an image view requires an image to do anything, therefore I should require an image on init!". However, this snippet here is a perfectly valid use case:
UIImageView *foo = [[UINavigationController alloc] init];
foo.image = [UIImage imageNamed:#"bar"];
Here we make an image view, and assign the image itself in a second step. So instead throw exceptions when you try to use your object in ways that require data to be present if that data isn't present.
Also, your designated initializer is usually the one that typically takes the most arguments, and the rest call that one. Something kind of like this may be more what you want.
- (id)init {
// call this class's designated initializer
return [self initWithSomething:nil];
}
// this class's designated initializer
- (id)initWithSomething:(NSString*)something {
self = [super init]; // call superclass's designated initializer
if (self) {
// this class's setup code goes here, saving any arguments
somethingInstanceVar = something;
}
return self;
}
// some other method.
- (NSString*)doSomething {
if (somethingInstanceVar) {
// all is well, do stuff
return somethingInstanceVar;
} else {
// never got a "something" raise an exception
[NSException raise:#"Missing data" format:#"Something is required!"];
}
}