Overriding a default init function in iOS - ios

originally, I have a default init function
-(id) init
{
if (self=[super init])
{
......
}
return self;
}
However, I like to override the init function to pass in custom objects or other objects
like
-(id) initWithScore:(NSString*) score
{
if (self=[super init])
Now there is an error saying [super init] function can only be called with -(id) init function.
So what do I do to fix it so I can pass in objects and also use self=[super init]?
Error:Cannot assign to self outside of a method in the init family.

I was trying to convert a projet to ARC and after creating a new one and including the files from the old one - one of the issues i got was
Cannot assign to 'self' outside of a method in the init family
The selector name MUST begin with init - not only that in my case the init selector was:
-(id)initwithPage:(unsigned)pageNum {...}
Notice the small 'w'.
I have changed it to:
-(id)initWithPage:(unsigned)pageNum {...}
Notice the capital 'W'!
My problem was solved.
I hope this helps someone.

You need to return an object of type id in your new method.
Suppose you have declare an NSString *myscore property, you will write something like this:
-(id) initWithScore:(NSString*) score
{
self=[super init];
if (self)
{
self.myscore = score;
}
return self;
}

Related

self init.. vs. super init... in subclasses

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.

Objective-c: Questions about self = [super init]

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...
}

obj-c inherited variables are null

I have a base class which has a custom init method and then uses the value passed in through the init method to then call custom init methods on it's subclasses. problem is When i try to access variables that have had values assigned to them in the base class through the subclass Via super, the values are null, it's like the base class is a completely different object. Is it because the base class has not yet returned from it's init method yet? or am i going about the wrong way with inheritance here? code to follow.
Interface
#interface WTFGameBoard : NSObject
{
#protected
UIView *_answerView;
UIView *_keyboardView;
NSMutableArray* _answerSeperatedByCharacter;
WTFAnswerBoard *_answerBoard;
WTFGameKeyboard *_gameKeyboard;
OpenGameViewController *_weakGameViewRef;
GameInfo *_gameinfo;
}
-(id) initWithGameVC:(OpenGameViewController*)gameVC;
#property (nonatomic,unsafe_unretained)OpenGameViewController *weakGameViewRef;
#property (nonatomic,strong)GameInfo *gameInfo;
#end
Implementation
#implementation WTFGameBoard
#synthesize weakGameViewRef = _weakGameViewRef;
#synthesize gameInfo = _gameinfo;
-(id) initWithGameVC:(OpenGameViewController*)gameVC
{
if (self = [super init])
{
//[weakGameViewRef ]
_answerView = [gameVC answerView];
_keyboardView = [gameVC keyboardView];
self.weakGameViewRef = gameVC;
self.gameInfo = [[CurrentGamesInfo sharedCurrentGamesInfo]_selectedGame];
_answerBoard = [[WTFAnswerBoard alloc] initWithAnswer:[gameVC answer] blankSpaceImageView:[gameVC answerBox]];
_gameKeyboard = [[WTFGameKeyboard alloc] initWithButtons:[gameVC letterSelectButtons]];
}
return self;
}
#end
Interface
#interface WTFAnswerBoard : WTFGameBoard
{
NSMutableArray *WTFAnswerSpaces;
NSMutableArray *_answerBlankBlocks;
NSMutableArray *_answerGiven;
NSMutableArray *_answerBlankOriginalPosition;
NSString *_answer;
}
-(id)initWithAnswer:(NSString*)answer blankSpaceImageView:(UIImageView*)answerBox;
Implementation
-(id)initWithAnswer:(NSString*)answer blankSpaceImageView:(UIImageView*)answerBox
{
if ( self = [super init] )
{
_weakGameViewRef = [super weakGameViewRef];//WHY U NO NOT BE NULL?
_gameinfo = [super gameInfo];//WHY U NO NOT BE NULL?
_answerBlankBlocks = [_weakGameViewRef answerBlankBlocks];
_answerGiven = [_weakGameViewRef answerGiven];
_answerBlankOriginalPosition = [_weakGameViewRef answerBlankOriginalPosition];
[self SetupBlankAnswerSpacesForAnswer:answer withTemplate:answerBox];
}
return self;
}
Problem is the fact that you are not invoking your custom constructor in your derived class:
if ( self = [super init] )
You are calling the default one, which is not overridden, and that doesn't initialize the ivars that you are trying to access.
You should call your custom constructor:
if ( self = [super initWithGameVC:gameVC] )
Of course this means that you need to pass along the parameter, or override the default constructor by initializing what you want to initialize without the need of any parameter.
Another thing that I don't understant is why you are setting ivars of the derived class in your custom class:
_weakGameViewRef = [super weakGameViewRef];
This basically does nothing because the ivar is the same, if you set the one of your base class then you can directly access it.
EDIT
Since you have a strange dependency issue here a quick solution would be to have something like
WTFAnswerBoard initWithWTFGameBoard:(WTFGameBoard*)board {
self.board = board;
}
so that you can access the board that instantiates the WTFAnswerBoard and keep inheritance but switching the usage to composition (by adding a property to WTFAnswerBoard so that your recursive initialization doesn't occur.

Objective-C multiple nested initWith for a custom class

I'm pretty new to Objective-C and I have a question.
I have created a custom class and tried to create overloads for Initialization:
- (id)init
{
if (self = [super init]) {
[self setIsCurrentCar:NO];
}
return self;
}
-(id) initWithID:(NSInteger)id {
if(self = [self init]) {
[self setID:id];
}
return self;
}
-(id) initWithID:(NSInteger)id CarYear:(NSString *)year {
if(self = [self initWithID:id]) {
[self setCarYear:year];
}
return self;
}
Let's say at one point, I call the -(id) initWithIDCarYear method.
I'd like to know the code above is structurally correct.
In this code, self is set for 3 times. Is there a better
solution?
Do I have memory leak in this code? (using ARC)
Do I have to check for if(self = ...) always or it is a
redundant code?
Thank you
#Edit
Is the following code better?
-(id) initWithID:(NSInteger)id CarYear:(NSString *)year {
if (self = [super init]) {
[self setIsCurrentCar:NO];
[self setID:id];
[self setCarYear:year];
}
return self;
}
While your code is ok, i would structure the init-calls in the reverse order, where the most detailed one is the designated initializer and the more general ones would bubble some default values up:
-(id) initWithID:(NSInteger)id
CarYear:(NSString *)year
{
if(self = [super init]) {
_year = year;
_id = id;
}
return self;
}
-(id)initWithID:(NSInteger)id
{
return [self initWithID:id CarYear:#"unset"];
}
-(id)init
{
return [self initWithID:0];
}
if calling one of the more general initializer would generate an illegal state, you could instead throw an error to prohibit using it.
let's assume, a car needs to have a ID, but not a year. It would be ok to use initWithID but using init would lead to an inconsistent state, so we want to force not to use it:
-(id)init
{
[NSException raise:NSInternalInconsistencyException
format:#"You must use -initWithID: or -initWithID:CarYear:", NSStringFromSelector(_cmd)];
return nil;
}
In this code, self is set for 3 times. Is there a better solution?
see above
Do I have memory leak in this code? (using ARC)
No, everything is fine
Do I have to check for if(self = ...) always or it is a redundant code?
As I showed you: you can call different init methods in a chain. just the last in that chain needs to perform that.
-(id) initWithID:(NSInteger)id CarYear:(NSString *)year {
if (self = [super init]) {
[self setIsCurrentCar:NO];
[self setID:id];
[self setCarYear:year];
}
return self;
}
You should not use setters on self in init-methods, see Apple's docs.
I'd like to know the code above is structurally correct.
Yes. I don't see any problem with it.
In this code, self is set for 3 times. Is there a better solution?
That's pretty normal. I wouldn't bother changing that.
Do I have memory leak in this code? (using ARC)
No.
Do I have to check for if (self = ...) always or it is a redundant
code?
You don't have to, but you definitely should. See this question for details.
It looks like you have one mandatory initialized variable and two that are effectively optional.
I'd recommend implementing an init method and two #property()s for the ID and carYear. That reduces the # of initializers and better reflects that usage contract of the class.
From my little knowledge... tried to create overloads for Initialization this statement should not been used here.
As typically overload means same name multiple arguments, but in obj-c we do not follow this. In obj-c overloading is faked by naming the parameters.
So here you have 3 different sets of code, and each one is called some other method. And you do not have a memory leak as you are not allocating memory thrice for same object, instead you are initializing it.

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.

Resources