I read that using properties in init method is considered as bad practice. But should I use parent class properites ?
For example
-(id) init
{
if (self = [super init])
{
self.parentProp = someVal; // (1)
parentProp = someVal; // (2)
}
return self;
}
What is prefered (1 or 2) and why? Thanks!
After you've called super's init method, and it has returned, the superclass's part of your object is initialized and ready for use. It's normal and expected that you use its property accessors after that. For example. If you make a subclass of UIViewController, it's normal to then set your (inherited) title property, or modify your navigationItem, in your init method.
That said, you can break this behavior. If you've overridden one of your superclass's methods (including one of its accessors methods), and then you call that method in your init method, it's up to you to be sure your overridden method will behave properly before your object is fully initialized.
More subtly, maybe you're overridden a superclass method, and then you call a different superclass method that you haven't overridden. Well, what if the method you call turns around and calls the method you have overridden? You need to be aware of this possibility too.
All that said, I reiterate that it's perfectly normal to use your superclass's property accessors to customize it after you have initialized it by calling one of its init methods.
To answer your question - neither of them.
(2) is not a property access, but direct instance variable access. It depends on the class hierarchy design, but in general I would strongly discourage from using ivars in non-private interfaces - for details, see this answer to related question
In general, you should not use any of the class public methods (including properties access) in the class initializer (and in the dealloc for that matter) - if you class hierarchy doesn't prohibit subclassing explicitly. Because if you do - the subclasses overriding these methods (or properties accessors) will get them called while being in invalid state (not yet initialized or already dealloc'ed).
While I've encountered a number of problems caused by pt.2 in general it seems to be a common practice to ignore it (i.e. to use self/parent class properties in initializer). So I would say it's up to you. Either write more code for explicit setup outside of your classes initializers and feel confident that you would never encounter this problem. Or have probably more simple/short initialization and easier usage of your class but stay aware of that problem.
Related
I have read up quite a bit on designated and convenience initializers in objective-c and feel I have a good understanding of how and why they are used. I just have a couple of outstanding questions, well one really.
Say you have a class that inherits directly from NSObject and you create a designated initializer for it.
I realise you can now mark your designated initializer using NS_DESIGNATED_INITIALIZER but I am wondering if that means it forces you to separately, within the same class, first override init with a call to your designated initializers?
Secondly, if you weren't to use this macro, what might the implications be of NOT overriding init explicitly with the call to the designated initializer? I realise the compiler would not know which was the designated initializer, so could anything bad come of this, and is it just good practice to also override init when you create a new designated initalizer for your class?
EDIT: I just want to add further clarification on this question. I have seen examples of good coders not adding a distinct method to override init despite having added a designated initialiser of their own making.
When I ask if you should override init I mean in addition to creating your own specific designated initialiser which of course should call its superclass's init method. I have seen a good coder not do this and so I wondered why you would therefore do it.
you can now mark your designated initializer using NS_DESIGNATED_INITIALIZER but I am wondering if that means it forces you to separately, within the same class, first override init with a call to your designated initializer?
Calling any initializer in self will bypass the warning. When I try to call a super.init method I do get the following warning.
Convenience initializer missing a 'self' call to another initializer.
what might the implications be of NOT overriding init explicitly with the call to the designated initializer?
This would mean your object might be missing some important information that it requires to function, or that the app assumes the object always has, and if it doesn't have it, it could cause errors or a crash in your app.
Here is a good explanation of how it works:
http://timekl.com/blog/2014/12/09/objective-cs-designated-secret/
Just looking at an example of code which implements a method called bodyMassIndex.
Rather than access the instance variables in the class directly, the idea is that accessor methods are used instead. I'm just not sure whether both setter and getter methods are present here and that's what I need to ask about.
Here is the code:
-(float)bodyMassIndex
float h = [self heightInMeters];
return [self weightInKilos] / (h*h);
What I am really wondering is where is the setter method in this code? I see the getter methods being used, in terms of the two messages heightInMeters and weightInKilos being sent to the instance of the class, but I'm not seeing the setter methods. Is it that the setter methods are not going to be used in the implementation of other methods?
I have seen the setters used in the related main.c file for this program so I know how they get used in terms of setting a value indirectly.
Are setters only used purely outside of the class then?
I really don't like to copy and paste code and ask about it. I'd rather ask questions about code I've written myself that I'm having problems with, but as I am new to accessor methods I haven't any choice this time!
This will (most likely) be the getter for a read only property, because it is computed and there is no backing iVar referenced. heightInMeters and weightInKilos are probably read/write and this is just a little helper, where there would be no point keeping track of it (bodyMassIndex) when it is just the product of two other properties, doing so just invited conflict and errors
I found some info of the designated initializer in this Apple's docs, but what I don't understand is, must each class have one and only one designated initializer?
For example, what if class A has initL, initM, initN, while class B inherits from class A and has initX, initY, initZ. Is there a rule that says we can't have initX call [super initL], and initY call [super initM], and initZ call [super initN]?
That is, instead of all "secondary initializers" call the designated initializer, and then each designated initializer will call the [super initFoo] where initFoo is the superclass's designated initializer, can't we just have 3 primary initializers, and each one caller its corresponding superclass's 3 primary initializers? (and say, these all inherit from NSObject and just call self = [super init].)
No, an obj-c class may have multiple designated initializers. The most common example of this is -initWithCoder: vs -init. The former is used when unarchiving an object, and the latter is used for all other initialization.
That said, it's generally good practice to only have one designated initializer outside of -initWithCoder:. This helps to prevent code duplication and makes it obvious which method a subclass has to override if they want to be invoked for all initializations. But if you have a good case for needing 3 distinct designated initializers, then there's nothing stopping you from doing it. Just be sure to document it properly.
Designated initializers are a concept that helps to prevent recursive calls and omitted important base class initialization. It is possible to not follow the designated initializer rules and still build a working class hierarchy.
In fact there are patterns in Cocoa that deviate from pure designated initializes: NSCoding for example requires to initialze objects using initWithCoder: but you can still initialize objects from code using the other initializers.
How can I prevent a method from getting overridden in a subclass, missing a call to its superclass' implementation within?.
I know calling [super methodName]; will solve my problem sometimes.
But if somebody else was to use my parent class and overrode my method, accidentally missing to call super, what can I do?
Some more explanations:
I create a viewcontroller VC1 which has a method -(void)indexDidChange:(int)index { }. I write some actions there which I need to perform every time. and I subclass this viewcontroller named as SVC1 in it I need -(void)indexDidChange:(int)index { } for doing some other actions but at the same time the VC1 -(void)indexDidChange:(int)index { } action also need to perform. So I need to call like,
-(void)indexDidChange:(int)index {
[super indexDidChange:index];
}
So I decide to change VC1 function like,
-(void)indexDidChange:(int)index {
[self currentIndexDidChange:(int)index];
}
-(void)currentIndexDidChange:(int)index { }
And I need -(void)currentIndexDidChange:(int)index { } to override and prevent -(void)indexDidChange:(int)index { } from overriding.
Is it possible?
Edit: After OP rephrased the question it is clear that OP is actually NOT looking for final methods, despite the questions initial phrasing, which implied just this.
New (updated) answer to OP's question on method overriding safety:
According to your rephrased question you are not looking for protecting a method from being overridden at all, but rather worried about one of your subclasses overriding a method and accidently missing to include a call to super in its new implementation.
This however is a fairly common and widespread issue and something you're dealing with on a daily basis, without paying much attention to it.
Every Objective-C programmer is familiar with the following method, right?
- (void)dealloc {
[iVar release], iVar = nil;
[super dealloc]; //skipping this call to super is fatal!
}
And we al know that skipping the [super dealloc]; makes things get uncomfortable. (afaik the clang compiler issues a warning if dealloc lacks the call to super, …pretty handy.)
Despite the fact that a bad overriding of this method can have fatal consequences Apple did not choose to put any kind of security system in place here.
Instead Apple did this (as done with any other method requiring calls to super):
Add a note to the method's documentation:
After performing the class-specific
deallocation, the subclass method
should incorporate superclass versions
of dealloc through a message to
super
Expect you, the programmer, to be a grown-up and responsible for what you do. And for playing by the rules (as defined by the documentation).
Keep in mind that - (void)dealloc is by no means an exception. There are dozens and dozens of methods of this type in Cocoa. (Take just about any derivative of - (id)init, most of the KVO observing methods, etc. just to name a few.)
So what you should do is:
Write a good documentation for your
method. (better for your entire project, actually)
Add a big loud note to your method's documentation, explaining its rules.
Add a note to each of your subclasses' overridden method implementations, right above the line that's calling super, telling the reader/dev to look up documentation, when in doubt of the rules. (optional)
Code responsibly. Otherwise, you shouldn't be coding in first place. It's your customers who will suffer from it, eventually.
Old (pre-rephrasing) answer on archieving pseudo-final methods:
What you are asking for is the equivalent of a final function, as known from Java or C++.
Unlike Java or C++, however there are no final methods in Objective-C.
Depending on your situation there are solutions that might bring your at least near to what you're aiming for. All you'll get though is slightly better separation. You won't get any significant security from them. In Objective-C you cannot even be sure about the origin of your methods. Method swizzling allows you to exchange methods at will. With code injection you an even inject code into processes at runtime. All this is by design of Objective-C. Objective-C allows you to saw off the branch you're sitting on. Thus it demands you to act like a grown-up. As such there are no private methods either. If a method is proclaim private you as a dev are expected to behave accordingly.
Now to possible "solutions":
If only your super class if supposed to call the given (final) method anyway:
Then Macmade's solution of making your method a pseudo-private method would work quite well. The downside of hiding method declarations though is, that calling your hidden method from subclasses will give you a compiler warning, basically preventing*(sic!)* you from calling it. (It will not prevent you from calling the method though. It will only avoid you from doing so, by throwing compiler warnings.)
If subclasses however are expected to call the given (final) method:
Use a delegation pattern and by this only make those methods public that are allowed to be overridden.
To prevent overriding at all you could use the class cluster & abstract factory patterns, which hides your implementation classes and thus preventing overriding entirely. (Apple's NSArray, NSDictionary, NSSet classes do this)
However you might notice that with Objective-C lack of protection one usually can only choose between the two: openness, protectedness, not intermix them.
You can use categories in the implementation, so your methods aren't exposed in your header file.
MyClass.m
#interface MyClass( Private )
- ( void )myMethod;
#end
#implementation MyClass( Private )
- ( void )myMethod
{}
#end
#implementation MyClass
/* ... */
#end
If you don't declare your function in the ".h file" then its not listed, I think.
There are a couple of things about Objective-C that are confusing to me:
Firstly, in the objective-c guide, it is very clear that each class needs to call the init method of its subclass. It's a little bit unclear about whether or not a class that inherits directly from NSObject needs to call its init method. Is this the case? And if so, why is that?
Secondly, in the section about NSObject, there's this warning:
A class that doesn’t need to inherit any special behavior from another class should nevertheless be made a subclass of the NSObject class. Instances of the class must at least have the ability to behave like Objective-C objects at runtime. Inheriting this ability from the NSObject class is much simpler and much more reliable than reinventing it in a new class definition.
Does this mean that I need to specify that all objects inherit from NSObject explicitly? Or is this like Java/Python/C# where all classes are subtypes of NSObject? If not, is there any reason to make a root class other than NSObject?
1) Any time an object is allocated in Objective-C its memory is zeroed out, and must be initialized by a call to init. Subclasses of NSObject may have their own specialized init routines, and at the beginning of such they should call their superclass' init routine something like so:
self = [super init];
The idea being that all init routines eventually trickle up to NSObject's init.
2) You need to be explicit about the inheritance:
#instance myClass : NSObject { /*...*/ } #end
There is no reason to have a root class other than NSObject -- a lot of Objective-C relies heavily on this class, so trying to circumvent it will result in you needlessly shooting yourself in the foot.
Since it is possible to inherit from different root base classes, yes you must explicitly declare you inherit from NSObject when you make any new class (unless of course you are subclassing something else already, which itself in turn probably subclasses NSObject).
Almost never is there a need to make your own base class, nor would it be easy to do so.
Objective-C can have multiple root classes, so you need to be explicit about inheritance. IIRC NSProxy is another root class. You'll likely never want or need to create your own root class, but they do exist.
As for calling NSObject's init, it's part custom and part safety. NSObject's init may not do anything now, that's no guarantee that future behaviour won't change. Call init to be safe.
You need to call [super init] because there is code behind initializing that you dont have to write because it is written for you in NSObjects init, such as probably actual memory allocation etc.