self init.. vs. super init... in subclasses - ios

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.

Related

How do I override init - message without creating unwanted recursion in Objective C?

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.

Calling init Method in Objective C

I'm just getting to the part of my Obj-C book where init methods are covered. I understand them, but I'm not sure where to call the method. I have a VC class that needs to have one of its #propertys set to another value, besides the default 0 before the rest of the code is executed. VC is a subclass of UIViewController.
Here is my code for the init method in VC.m:
- (id)initmethod
{
if((self = [super init])){
self.value = -1 ;
}
return self ;
}
Where do I call this? Do I need to have a sub class that calls it? Or do I need to manually create the VC object that is linked to the view and call this method there?
Yes you need to call this when you create the controller,
VC *vcInstance = [[VC alloc] initMethod];
There's really no need to create your own init method though, you can just override init.
If you're making your controller in a storyboard however, you would want to override initWithCoder, and set your value property there.
-(id)initWithCoder:(NSCoder *)aDecoder {
if (self = [super initWithCoder:aDecoder]) {
_value = -1; // you should use the ivar rather than self.propertyName inside an init method
}
return self;
}
Likewise, if the controller is made in a xib file you should override initWithNibName:bundle:

about parent object's init method

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.

Is there a init method in iOS that is always called?

Is there a method that is always called in Cocoa? Many classes have init or initWith, but even worse they can be loaded from a nib or something. I don't want to have to scrape around and find how it does this in this case. I just want to set some initial variables and other things, and I want a method to subclass that I can depend on no matter if it's a UIView, UIViewController or UITableViewCell etc.
No there is not such a method. init comes from NSObject so every object can use it, and as well subclasses define their own initialization methods. UIView, for example, defines initWithFrame: and furthermore there are init methods from protocols, such as NSCoding which defines initWithCoder:. This is the dynamic nature of objective-C, anything can be extended at any time. That being said, there are some patterns. UIViewController almost always takes initWithNibName:bundle: and UIView almost always takes initWithFrame: or initWithCoder:. What I do is make an internal initialize method, and just have the other inits call it.
- (void)initialize
{
//Do stuff
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if(self)
{
[self initialize];
}
}
- (id)initWithCoder:(NSCoder *)aCoder
{
self = [super initWithCoder:aCoder];
if(self)
{
[self initialize];
}
}
Not 100% sure that it is always called, but I am pretty sure that this is a viable option. To be perfectly honest, I can't recall that I have ever seen this method used in practice and I usually shy away from using this method (I have absolutely no idea why, probably because it's just not the cleanest and most comprehensive method to achieve this...):
-didMoveToSuperview()
From documentation:
Tells the view that its superview changed.
The default implementation of this method does nothing. Subclasses can override it to perform additional actions whenever the superview changes.
There's many ways you can write a custom initializer.
- (id)initWithString:(NSString *)string {
if((self == [super init])) {
self.string = string;
}
return self;
}
That's just how I write my initializers in general. For example, the one above takes a string. (you don't have to pass strings if you don't want).
Btw, init is a method. According to the header for NSObject, init has a method implementation.

iOS: UIView subclass init or initWithFrame:?

I made a subclass of UIView that has a fixed frame. So, can I just override init instead of initWithFrame:? E.g.:
- (id)init {
if ((self = [super initWithFrame:[[UIScreen mainScreen] bounds]])) {
self.backgroundColor = [UIColor clearColor];
}
return self;
}
The Xcode documentation for -initWithFrame: says: "If you create a view object programmatically, this method is the designated initializer for the UIView class. Subclasses can override this method to perform any custom initialization but must call super at the beginning of their implementation."
What does "designated initializer" mean?
The designated initializer is the one that all the other initializers must call. UIView and subclasses are a little unusual in that they've actually got two such initializers: -initWithFrame: and -initWithCoder:, depending on how the view is created. You should override -initWithFrame: if you're instantiating the view in code, and -initWithCoder: if you're loading it from a nib. Or, you could put your code in third method and override both those initializers such that they call your third method. In fact, that's often the recommended strategy.
So, for example, you might create a UIView subclass, ClueCharacter, that has its own initialization method: -initWithPerson:place:thing:. You then create your view like this:
Obj-C:
ClueCharacter *mustard = [[ClueCharacter alloc] initWithPerson:#"Col. Mustard"
place:kInTheStudy
thing:kTheRope];
Swift:
var mustard = ClueCharacter("Col. Mustard", place: kInTheStudy, thing: kTheRope)
That's fine, but in order to initialize the UIView part of the object, your method must call the designated initializer:
Obj-C:
-(id)initWithPerson:(NSString*)name place:(CluePlace)place thing:(ClueWeapon)thing
{
if ((self = [super initWithFrame:CGRectMake(0, 0, 150, 200)])) {
// your init stuff here
}
}
Swift:
func init(name: String, place : CluePlace, thing : ClueWeapon)
{
if (self = super.init(CGRectMake(0, 0, 150, 200))) {
// your init stuff here
}
}
If you want to call your subclass's initializer -init, that's okay as long as you call -initWithFrame: in the implementation.
in UIView calling [super init] is exactly equal to [super initWithFrame:CGRectZero]
In an Objective-C class with multiple initialisers, the designated initialiser is the one that does the meaningful work. So, often you have a class with a few initialisers, say:
- (id)initWithRect:(CGRect)someRect;
- (id)initWithRect:(CGRect)someRect setDefaultColour:(BOOL)setDefaultColour;
- (id)initWithRect:(CGRect)someRect setDefaultColour:(BOOL)setDefaultColour
linkTo:(id)someOtherObject;
In that case you'd normally (but not always) say that the third was the designated initialiser, and implement the other two as e.g.
- (id)initWithRect:(CGRect)someRect
{
return [self initWithRect:someRect setDefaultColour:NO];
}
- (id)initWithRect:(CGRect)someRect setDefaultColour:(BOOL)setDefaultColour
{
return [self initWithRect:someRect setDefaultColour:setDefaultColour
linkTo:nil];
}
If a class has only one initialiser then that's the designated initialiser.
In your case to follow best practice you should implement initWithFrame: and also a vanilla init: that calls initWithFrame: with your normal dimensions. The normal convention is that you can add new variations on init in subclasses, but shouldn't take any away, and that you always do the actual initialising work in the designated initialiser. That allows any initialising methods from the parent class that you don't provide new implementations of still to work appropriately with your subclass.

Resources